hlarcher HF Staff commited on
Commit
feb8590
·
unverified ·
1 Parent(s): bdb8bb2

feat: enhance bandwidth test with client and server information display

Browse files
src/app.html CHANGED
@@ -1,12 +1,18 @@
1
  <!doctype html>
2
  <html lang="en">
3
- <head>
4
- <meta charset="utf-8" />
5
- <link rel="icon" href="%sveltekit.assets%/favicon.png" />
6
- <meta name="viewport" content="width=device-width, initial-scale=1" />
7
- %sveltekit.head%
8
- </head>
9
- <body data-sveltekit-preload-data="hover" class="bg-gray-50 min-h-screen">
10
- <div class="container mx-auto px-4 py-8">%sveltekit.body%</div>
11
- </body>
 
 
 
 
 
 
12
  </html>
 
1
  <!doctype html>
2
  <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <title>Hugging Face bandwidth test</title>
6
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
7
+ <link rel="icon" type="image/png" href="%sveltekit.assets%/favicon-96x96.png" sizes="96x96" />
8
+ <link rel="icon" type="image/svg+xml" href="%sveltekit.assets%/favicon.svg" />
9
+ <link rel="shortcut icon" href="%sveltekit.assets%/favicon.ico" />
10
+ <link rel="apple-touch-icon" sizes="180x180" href="%sveltekit.assets%/apple-touch-icon.png" />
11
+ <meta name="apple-mobile-web-app-title" content="Hugging Face fast" />
12
+ <link rel="manifest" href="%sveltekit.assets%/site.webmanifest" />
13
+ %sveltekit.head%
14
+ </head>
15
+ <body data-sveltekit-preload-data="hover" class="bg-gray-50 min-h-screen">
16
+ <div class="container mx-auto px-4 py-8">%sveltekit.body%</div>
17
+ </body>
18
  </html>
src/lib/icao.ts ADDED
@@ -0,0 +1,236 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ interface AirportDetails {
2
+ city: string;
3
+ country: string; // ISO 3166-1 alpha-2 code
4
+ }
5
+
6
+ type MajorAirportIATAsWithCountry = {
7
+ [iataCode: string]: AirportDetails;
8
+ };
9
+
10
+ export const majorAirportIATAs:MajorAirportIATAsWithCountry = {
11
+ // USA
12
+ "LAX": { "city": "Los Angeles", "country": "US" },
13
+ "JFK": { "city": "New York", "country": "US" },
14
+ "ORD": { "city": "Chicago", "country": "US" },
15
+ "ATL": { "city": "Atlanta", "country": "US" },
16
+ "DFW": { "city": "Dallas", "country": "US" },
17
+ "DEN": { "city": "Denver", "country": "US" },
18
+ "SFO": { "city": "San Francisco", "country": "US" },
19
+ "SEA": { "city": "Seattle", "country": "US" },
20
+ "MIA": { "city": "Miami", "country": "US" },
21
+ "LAS": { "city": "Las Vegas", "country": "US" },
22
+ "MCO": { "city": "Orlando", "country": "US" },
23
+ "EWR": { "city": "Newark", "country": "US" },
24
+ "CLT": { "city": "Charlotte", "country": "US" },
25
+ "PHX": { "city": "Phoenix", "country": "US" },
26
+ "IAH": { "city": "Houston", "country": "US" },
27
+ "BOS": { "city": "Boston", "country": "US" },
28
+ "MSP": { "city": "Minneapolis", "country": "US" },
29
+ "DTW": { "city": "Detroit", "country": "US" },
30
+ "PHL": { "city": "Philadelphia", "country": "US" },
31
+ "LGA": { "city": "New York", "country": "US" },
32
+ "BWI": { "city": "Baltimore", "country": "US" },
33
+ "SLC": { "city": "Salt Lake City", "country": "US" },
34
+ "IAD": { "city": "Washington D.C.", "country": "US" },
35
+ "DCA": { "city": "Washington D.C.", "country": "US" },
36
+ "SAN": { "city": "San Diego", "country": "US" },
37
+ "TPA": { "city": "Tampa", "country": "US" },
38
+ "HNL": { "city": "Honolulu", "country": "US" },
39
+ // Canada
40
+ "YYZ": { "city": "Toronto", "country": "CA" },
41
+ "YVR": { "city": "Vancouver", "country": "CA" },
42
+ "YUL": { "city": "Montreal", "country": "CA" },
43
+ "YYC": { "city": "Calgary", "country": "CA" },
44
+ "YEG": { "city": "Edmonton", "country": "CA" },
45
+ "YOW": { "city": "Ottawa", "country": "CA" },
46
+ "YWG": { "city": "Winnipeg", "country": "CA" },
47
+ "YHZ": { "city": "Halifax", "country": "CA" },
48
+ // United Kingdom
49
+ "LHR": { "city": "London", "country": "GB" },
50
+ "LGW": { "city": "London", "country": "GB" },
51
+ "STN": { "city": "London", "country": "GB" },
52
+ "LTN": { "city": "London", "country": "GB" },
53
+ "MAN": { "city": "Manchester", "country": "GB" },
54
+ "BHX": { "city": "Birmingham", "country": "GB" },
55
+ "EDI": { "city": "Edinburgh", "country": "GB" },
56
+ "GLA": { "city": "Glasgow", "country": "GB" },
57
+ // France
58
+ "CDG": { "city": "Paris", "country": "FR" },
59
+ "ORY": { "city": "Paris", "country": "FR" },
60
+ "NCE": { "city": "Nice", "country": "FR" },
61
+ "LYS": { "city": "Lyon", "country": "FR" },
62
+ "MRS": { "city": "Marseille", "country": "FR" },
63
+ "TLS": { "city": "Toulouse", "country": "FR" },
64
+ "NTE": { "city": "Nantes", "country": "FR" },
65
+ "BOD": { "city": "Bordeaux", "country": "FR" },
66
+ // Germany
67
+ "FRA": { "city": "Frankfurt", "country": "DE" },
68
+ "MUC": { "city": "Munich", "country": "DE" },
69
+ "BER": { "city": "Berlin", "country": "DE" },
70
+ "DUS": { "city": "Düsseldorf", "country": "DE" },
71
+ "HAM": { "city": "Hamburg", "country": "DE" },
72
+ "CGN": { "city": "Cologne", "country": "DE" },
73
+ "STR": { "city": "Stuttgart", "country": "DE" },
74
+ // China (Mainland)
75
+ "PEK": { "city": "Beijing", "country": "CN" },
76
+ "PKX": { "city": "Beijing", "country": "CN" },
77
+ "PVG": { "city": "Shanghai", "country": "CN" },
78
+ "SHA": { "city": "Shanghai", "country": "CN" },
79
+ "CAN": { "city": "Guangzhou", "country": "CN" },
80
+ "CTU": { "city": "Chengdu", "country": "CN" }, // Shuangliu
81
+ "TFU": { "city": "Chengdu", "country": "CN" }, // Tianfu
82
+ "SZX": { "city": "Shenzhen", "country": "CN" },
83
+ "CKG": { "city": "Chongqing", "country": "CN" },
84
+ "WUH": { "city": "Wuhan", "country": "CN" },
85
+ "XIY": { "city": "Xi'an", "country": "CN" },
86
+ "HGH": { "city": "Hangzhou", "country": "CN" },
87
+ // India
88
+ "DEL": { "city": "Delhi", "country": "IN" },
89
+ "BOM": { "city": "Mumbai", "country": "IN" },
90
+ "BLR": { "city": "Bengaluru", "country": "IN" },
91
+ "MAA": { "city": "Chennai", "country": "IN" },
92
+ "CCU": { "city": "Kolkata", "country": "IN" },
93
+ "HYD": { "city": "Hyderabad", "country": "IN" },
94
+ // Brazil
95
+ "GRU": { "city": "Sao Paulo", "country": "BR" },
96
+ "GIG": { "city": "Rio de Janeiro", "country": "BR" },
97
+ "BSB": { "city": "Brasilia", "country": "BR" },
98
+ "CNF": { "city": "Belo Horizonte", "country": "BR" },
99
+ "SSA": { "city": "Salvador", "country": "BR" },
100
+ "FOR": { "city": "Fortaleza", "country": "BR" },
101
+ "POA": { "city": "Porto Alegre", "country": "BR" },
102
+ "REC": { "city": "Recife", "country": "BR" },
103
+ "CWB": { "city": "Curitiba", "country": "BR" },
104
+ // Australia
105
+ "SYD": { "city": "Sydney", "country": "AU" },
106
+ "MEL": { "city": "Melbourne", "country": "AU" },
107
+ "BNE": { "city": "Brisbane", "country": "AU" },
108
+ "PER": { "city": "Perth", "country": "AU" },
109
+ "ADL": { "city": "Adelaide", "country": "AU" },
110
+ "CBR": { "city": "Canberra", "country": "AU" },
111
+ // Japan
112
+ "NRT": { "city": "Tokyo", "country": "JP" }, // Narita
113
+ "HND": { "city": "Tokyo", "country": "JP" }, // Haneda
114
+ "KIX": { "city": "Osaka", "country": "JP" }, // Kansai
115
+ "ITM": { "city": "Osaka", "country": "JP" }, // Itami
116
+ "CTS": { "city": "Sapporo", "country": "JP" }, // New Chitose
117
+ "FUK": { "city": "Fukuoka", "country": "JP" },
118
+ "OKA": { "city": "Okinawa", "country": "JP" }, // Naha
119
+ "NGO": { "city": "Nagoya", "country": "JP" }, // Chubu Centrair
120
+ // South Africa
121
+ "JNB": { "city": "Johannesburg", "country": "ZA" },
122
+ "CPT": { "city": "Cape Town", "country": "ZA" },
123
+ "DUR": { "city": "Durban", "country": "ZA" }, // King Shaka
124
+ // Netherlands
125
+ "AMS": { "city": "Amsterdam", "country": "NL" },
126
+ // Spain
127
+ "MAD": { "city": "Madrid", "country": "ES" },
128
+ "BCN": { "city": "Barcelona", "country": "ES" },
129
+ "PMI": { "city": "Palma de Mallorca", "country": "ES" },
130
+ "AGP": { "city": "Malaga", "country": "ES" },
131
+ "VLC": { "city": "Valencia", "country": "ES" },
132
+ // Italy
133
+ "FCO": { "city": "Rome", "country": "IT" }, // Fiumicino
134
+ "MXP": { "city": "Milan", "country": "IT" }, // Malpensa
135
+ "LIN": { "city": "Milan", "country": "IT" }, // Linate
136
+ "BLQ": { "city": "Bologna", "country": "IT" },
137
+ "NAP": { "city": "Naples", "country": "IT" },
138
+ "VCE": { "city": "Venice", "country": "IT" }, // Marco Polo
139
+ "PSA": { "city": "Pisa", "country": "IT" },
140
+ // Russia
141
+ "SVO": { "city": "Moscow", "country": "RU" }, // Sheremetyevo
142
+ "DME": { "city": "Moscow", "country": "RU" }, // Domodedovo
143
+ "VKO": { "city": "Moscow", "country": "RU" }, // Vnukovo
144
+ "LED": { "city": "Saint Petersburg", "country": "RU" }, // Pulkovo
145
+ "AER": { "city": "Sochi", "country": "RU" },
146
+ // United Arab Emirates
147
+ "DXB": { "city": "Dubai", "country": "AE" },
148
+ "AUH": { "city": "Abu Dhabi", "country": "AE" },
149
+ // Singapore
150
+ "SIN": { "city": "Singapore", "country": "SG" },
151
+ // Hong Kong
152
+ "HKG": { "city": "Hong Kong", "country": "HK" },
153
+ // South Korea
154
+ "ICN": { "city": "Seoul", "country": "KR" }, // Incheon
155
+ "GMP": { "city": "Seoul", "country": "KR" }, // Gimpo
156
+ "CJU": { "city": "Jeju", "country": "KR" },
157
+ // Turkey
158
+ "IST": { "city": "Istanbul", "country": "TR" }, // Istanbul Airport
159
+ "SAW": { "city": "Istanbul", "country": "TR" }, // Sabiha Gökçen
160
+ "AYT": { "city": "Antalya", "country": "TR" },
161
+ "ESB": { "city": "Ankara", "country": "TR" },
162
+ "ADB": { "city": "Izmir", "country": "TR" },
163
+ // Switzerland
164
+ "ZRH": { "city": "Zurich", "country": "CH" },
165
+ "GVA": { "city": "Geneva", "country": "CH" },
166
+ // Argentina
167
+ "EZE": { "city": "Buenos Aires", "country": "AR" }, // Ezeiza
168
+ "AEP": { "city": "Buenos Aires", "country": "AR" }, // Aeroparque
169
+ // Mexico
170
+ "MEX": { "city": "Mexico City", "country": "MX" },
171
+ "CUN": { "city": "Cancun", "country": "MX" },
172
+ "GDL": { "city": "Guadalajara", "country": "MX" },
173
+ "MTY": { "city": "Monterrey", "country": "MX" },
174
+ // Thailand
175
+ "BKK": { "city": "Bangkok", "country": "TH" }, // Suvarnabhumi
176
+ "DMK": { "city": "Bangkok", "country": "TH" }, // Don Mueang
177
+ "HKT": { "city": "Phuket", "country": "TH" },
178
+ "CNX": { "city": "Chiang Mai", "country": "TH" },
179
+ // Malaysia
180
+ "KUL": { "city": "Kuala Lumpur", "country": "MY" },
181
+ // Ireland
182
+ "DUB": { "city": "Dublin", "country": "IE" },
183
+ "SNN": { "city": "Shannon", "country": "IE" },
184
+ // Portugal
185
+ "LIS": { "city": "Lisbon", "country": "PT" },
186
+ "OPO": { "city": "Porto", "country": "PT" },
187
+ "FAO": { "city": "Faro", "country": "PT" },
188
+ // New Zealand
189
+ "AKL": { "city": "Auckland", "country": "NZ" },
190
+ "CHC": { "city": "Christchurch", "country": "NZ" },
191
+ "WLG": { "city": "Wellington", "country": "NZ" },
192
+ // Qatar
193
+ "DOH": { "city": "Doha", "country": "QA" },
194
+ // Saudi Arabia
195
+ "JED": { "city": "Jeddah", "country": "SA" },
196
+ "RUH": { "city": "Riyadh", "country": "SA" },
197
+ "DMM": { "city": "Dammam", "country": "SA" },
198
+ // Egypt
199
+ "CAI": { "city": "Cairo", "country": "EG" },
200
+ // Nigeria
201
+ "LOS": { "city": "Lagos", "country": "NG" },
202
+ "ABV": { "city": "Abuja", "country": "NG" },
203
+ // Kenya
204
+ "NBO": { "city": "Nairobi", "country": "KE" }, // Jomo Kenyatta
205
+ // Ethiopia
206
+ "ADD": { "city": "Addis Ababa", "country": "ET" },
207
+ // Colombia
208
+ "BOG": { "city": "Bogota", "country": "CO" },
209
+ "MDE": { "city": "Medellin", "country": "CO" }, // José María Córdova
210
+ // Chile
211
+ "SCL": { "city": "Santiago", "country": "CL" },
212
+ // Peru
213
+ "LIM": { "city": "Lima", "country": "PE" },
214
+ // Austria
215
+ "VIE": { "city": "Vienna", "country": "AT" },
216
+ // Belgium
217
+ "BRU": { "city": "Brussels", "country": "BE" },
218
+ // Czech Republic
219
+ "PRG": { "city": "Prague", "country": "CZ" },
220
+ // Denmark
221
+ "CPH": { "city": "Copenhagen", "country": "DK" },
222
+ // Finland
223
+ "HEL": { "city": "Helsinki", "country": "FI" },
224
+ // Greece
225
+ "ATH": { "city": "Athens", "country": "GR" },
226
+ // Hungary
227
+ "BUD": { "city": "Budapest", "country": "HU" },
228
+ // Norway
229
+ "OSL": { "city": "Oslo", "country": "NO" },
230
+ // Poland
231
+ "WAW": { "city": "Warsaw", "country": "PL" }, // Chopin
232
+ "KRK": { "city": "Krakow", "country": "PL" },
233
+ // Sweden
234
+ "ARN": { "city": "Stockholm", "country": "SE" } // Arlanda
235
+ };
236
+
src/lib/index.ts CHANGED
@@ -1,5 +1,7 @@
1
  // place files you want to import through the `$lib` alias in this folder.
2
 
 
 
3
  interface BandwidthCallback {
4
  (
5
  elapsedMs: number,
@@ -12,13 +14,15 @@ interface BandwidthCallback {
12
 
13
  export async function bandwidthTest(
14
  onProgress: BandwidthCallback,
15
- onLatency: (latency: number) => void
 
16
  ) {
17
  // performance.setResourceTimingBufferSize(100);
18
  // performance.clearResourceTimings();
19
  // const url = 'https://cdn-test-cloudfront.hf.co/5gb.safetensors';
20
  const url = 'https://cdn-test-cloudfront.hf.co/15mb.json';
21
- // first HEAD to get latency
 
22
  let startTime = performance.now();
23
  const latencyResponse = await fetch(url, { method: 'HEAD' });
24
  if (!latencyResponse.ok) {
@@ -43,8 +47,26 @@ export async function bandwidthTest(
43
  // const latency = responseStart - requestStart;
44
  // onLatency(latency);
45
  // }, 2000);
 
 
46
  const contentLengthHeader = response.headers.get('content-length');
47
  const totalBytes = contentLengthHeader ? parseInt(contentLengthHeader, 10) : 1e99;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  const reader = response.body.getReader();
49
  let loadedBytes = 0;
50
  let lastTimestamp = performance.now();
@@ -91,21 +113,30 @@ export async function bandwidthTest(
91
  }
92
  }
93
 
94
- export async function counter(callback: BandwidthCallback) {
95
- // start timer
96
- const startTime = performance.now();
97
- let counter = 0;
98
- // set a random value between 50 and 150
99
- const interval = setInterval(() => {
100
- counter = Math.floor(Math.random() * 100) + 50;
101
- // Calculate elapsed time in milliseconds
102
- const elapsedMs = performance.now() - startTime;
103
- callback(elapsedMs, 100, 100, counter, false);
104
- }, 1000);
105
- // Stop the counter after 3600 seconds
106
- setTimeout(() => {
107
- clearInterval(interval);
108
- const elapsedMs = performance.now() - startTime;
109
- callback(elapsedMs, 100, 100, counter, true);
110
- }, 3600 * 1000);
 
 
 
 
 
 
 
 
 
111
  }
 
1
  // place files you want to import through the `$lib` alias in this folder.
2
 
3
+ import { majorAirportIATAs } from '$lib/icao';
4
+
5
  interface BandwidthCallback {
6
  (
7
  elapsedMs: number,
 
14
 
15
  export async function bandwidthTest(
16
  onProgress: BandwidthCallback,
17
+ onLatency: (latency: number) => void,
18
+ onServerLocation: (location: string) => void
19
  ) {
20
  // performance.setResourceTimingBufferSize(100);
21
  // performance.clearResourceTimings();
22
  // const url = 'https://cdn-test-cloudfront.hf.co/5gb.safetensors';
23
  const url = 'https://cdn-test-cloudfront.hf.co/15mb.json';
24
+
25
+ // issue a HEAD request to estimate latency (round trip time)
26
  let startTime = performance.now();
27
  const latencyResponse = await fetch(url, { method: 'HEAD' });
28
  if (!latencyResponse.ok) {
 
47
  // const latency = responseStart - requestStart;
48
  // onLatency(latency);
49
  // }, 2000);
50
+
51
+ // extract content-length
52
  const contentLengthHeader = response.headers.get('content-length');
53
  const totalBytes = contentLengthHeader ? parseInt(contentLengthHeader, 10) : 1e99;
54
+
55
+ // extract pop location
56
+ let cdnPop = response.headers.get('x-amz-cf-pop');
57
+ if (cdnPop !== null) {
58
+ cdnPop = cdnPop.toUpperCase().slice(0, 3);
59
+ // try to map to IATA
60
+ if (cdnPop in majorAirportIATAs) {
61
+ cdnPop = majorAirportIATAs[cdnPop].city + ', ' + majorAirportIATAs[cdnPop].country;
62
+ } else {
63
+ cdnPop = 'Unknown';
64
+ }
65
+ } else {
66
+ cdnPop = 'Unknown';
67
+ }
68
+ onServerLocation(cdnPop);
69
+
70
  const reader = response.body.getReader();
71
  let loadedBytes = 0;
72
  let lastTimestamp = performance.now();
 
113
  }
114
  }
115
 
116
+ interface ClientInfo {
117
+ clientIp: string;
118
+ clientLocation: string;
119
+ }
120
+
121
+ export async function getClientInfo(): Promise<ClientInfo> {
122
+ let clientIp = 'Detecting...';
123
+ let clientLocation = 'Detecting...';
124
+ const res = await fetch('https://ipapi.co/json/');
125
+ clientIp = 'Not available';
126
+ clientLocation = 'Not available';
127
+ if (res.ok) {
128
+ const data = await res.json();
129
+ clientIp = data.ip || 'Unknown';
130
+ let location = '';
131
+ if (data.city) location += data.city + ', ';
132
+ if (data.region) location += data.region + ', ';
133
+ if (data.country_name) location += data.country_name;
134
+ clientLocation = location || 'Unknown';
135
+ }
136
+ return new Promise((resolve) =>
137
+ resolve({
138
+ clientIp,
139
+ clientLocation
140
+ })
141
+ );
142
  }
src/routes/+layout.svelte CHANGED
@@ -9,7 +9,7 @@
9
  <header class="text-center mb-10">
10
  <div class="flex justify-center items-center mb-4">
11
  <img src="https://huggingface.co/front/assets/huggingface_logo-noborder.svg" alt="Hugging Face Logo" class="h-12 mr-3">
12
- <h1 class="text-3xl md:text-4xl font-bold text-gray-800">Bandwidth Monitor</h1>
13
  </div>
14
  <p class="text-gray-600 max-w-2xl mx-auto">
15
  Measure your connection speed to Hugging Face's servers in real-time
 
9
  <header class="text-center mb-10">
10
  <div class="flex justify-center items-center mb-4">
11
  <img src="https://huggingface.co/front/assets/huggingface_logo-noborder.svg" alt="Hugging Face Logo" class="h-12 mr-3">
12
+ <h1 class="text-3xl md:text-4xl font-bold text-gray-800">Bandwidth Test</h1>
13
  </div>
14
  <p class="text-gray-600 max-w-2xl mx-auto">
15
  Measure your connection speed to Hugging Face's servers in real-time
src/routes/+page.svelte CHANGED
@@ -1,16 +1,31 @@
1
  <script lang="ts">
2
- import { bandwidthTest, counter } from '$lib';
3
  import { Chart, registerables } from 'chart.js';
4
  import type { Action } from 'svelte/action';
 
5
 
6
  Chart.register(...registerables);
7
 
 
 
8
  let currentBandwidth = $state('0');
9
  let currentLatency = $state(0);
 
 
 
10
  let progress = $state(0);
11
  let bandwidthMeasurements: number[] = $state([]);
12
  let timeMeasurements: string[] = $state([]);
13
  let testStatus = $state('Idle');
 
 
 
 
 
 
 
 
 
14
  let bandwidthCallback = (elapsedMs: number, loadedBytes: number, totalBytes: number, bw: number, done: boolean) => {
15
  let mbps = (bw / 1000000 * 8); // convert Bps to Mbps
16
  // update the bandwidth state
@@ -40,6 +55,11 @@
40
  // update the latency state
41
  currentLatency = latency;
42
  };
 
 
 
 
 
43
 
44
  const startTest = () => {
45
  testStatus = 'Running';
@@ -48,11 +68,15 @@
48
  timeMeasurements = [];
49
  currentBandwidth = '0';
50
  currentLatency = 0;
51
- bandwidthTest(bandwidthCallback, latencyCallback);
 
 
 
52
  };
53
  const stopTest = () => {
54
  testStatus = 'Stopped';
55
- }
 
56
  //counter(callback);
57
  //bandwidthTest(bandwidthCallback, latencyCallback);
58
 
@@ -184,8 +208,8 @@
184
  <!-- Test Controls -->
185
  <div class="flex flex-col sm:flex-row justify-center gap-4">
186
  <button id="start-test"
187
- class="bg-indigo-600 hover:bg-indigo-700 text-white font-medium py-3 px-6 rounded-lg transition-all flex items-center justify-center glow"
188
- onclick={startTest}>
189
  <i class="fas fa-play mr-2"></i>
190
  Start Test
191
  </button>
@@ -198,6 +222,48 @@
198
  </div>
199
  </div>
200
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
  <!-- Results Graph -->
202
  <div class="bg-white rounded-xl shadow-lg overflow-hidden mb-8">
203
  <div class="p-6 md:p-8">
 
1
  <script lang="ts">
2
+ import { bandwidthTest, getClientInfo } from '$lib';
3
  import { Chart, registerables } from 'chart.js';
4
  import type { Action } from 'svelte/action';
5
+ import { onMount } from 'svelte';
6
 
7
  Chart.register(...registerables);
8
 
9
+ const MaxTestDurationSec = 20;
10
+
11
  let currentBandwidth = $state('0');
12
  let currentLatency = $state(0);
13
+ let serverLocation = $state('-');
14
+ let clientIp = $state('Detecting...');
15
+ let clientLocation = $state('Detecting...');
16
  let progress = $state(0);
17
  let bandwidthMeasurements: number[] = $state([]);
18
  let timeMeasurements: string[] = $state([]);
19
  let testStatus = $state('Idle');
20
+
21
+ // run ip info to get client IP and location
22
+ onMount(async () => {
23
+ let info = await getClientInfo();
24
+ clientIp = info.clientIp;
25
+ clientLocation = info.clientLocation;
26
+ });
27
+
28
+ // define callbacks
29
  let bandwidthCallback = (elapsedMs: number, loadedBytes: number, totalBytes: number, bw: number, done: boolean) => {
30
  let mbps = (bw / 1000000 * 8); // convert Bps to Mbps
31
  // update the bandwidth state
 
55
  // update the latency state
56
  currentLatency = latency;
57
  };
58
+ let serverLocationCallback = (location: string) => {
59
+ serverLocation = location;
60
+ };
61
+
62
+ let testTimeoutHandler = 0;
63
 
64
  const startTest = () => {
65
  testStatus = 'Running';
 
68
  timeMeasurements = [];
69
  currentBandwidth = '0';
70
  currentLatency = 0;
71
+ bandwidthTest(bandwidthCallback, latencyCallback, serverLocationCallback);
72
+ testTimeoutHandler = setTimeout(() => {
73
+ testStatus = 'Completed';
74
+ }, MaxTestDurationSec * 1000);
75
  };
76
  const stopTest = () => {
77
  testStatus = 'Stopped';
78
+ clearTimeout(testTimeoutHandler);
79
+ };
80
  //counter(callback);
81
  //bandwidthTest(bandwidthCallback, latencyCallback);
82
 
 
208
  <!-- Test Controls -->
209
  <div class="flex flex-col sm:flex-row justify-center gap-4">
210
  <button id="start-test"
211
+ class="{testStatus == 'Running'?'bg-indigo-100 hover:bg-indigo-100':'bg-indigo-600 hover:bg-indigo-700 glow'} text-white font-medium py-3 px-6 rounded-lg transition-all flex items-center justify-center"
212
+ onclick={startTest} disabled={testStatus!=='Idle'}>
213
  <i class="fas fa-play mr-2"></i>
214
  Start Test
215
  </button>
 
222
  </div>
223
  </div>
224
  </div>
225
+ <!-- Connection Info Card -->
226
+ <div class="bg-white rounded-xl shadow-lg overflow-hidden mb-8 connection-info">
227
+ <div class="p-6 md:p-8">
228
+ <h2 class="text-xl font-semibold text-gray-800 mb-6">Connection Information</h2>
229
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
230
+ <div>
231
+ <h3 class="text-sm font-medium text-gray-500 mb-2">CLIENT INFORMATION</h3>
232
+ <div class="flex items-center mb-3">
233
+ <i class="fas fa-laptop text-indigo-500 mr-3"></i>
234
+ <div>
235
+ <div class="text-sm text-gray-500">IP Address</div>
236
+ <div id="client-ip" class="text-lg font-medium text-gray-800">{clientIp}</div>
237
+ </div>
238
+ </div>
239
+ <div class="flex items-center">
240
+ <i class="fas fa-map-marker-alt text-indigo-500 mr-3"></i>
241
+ <div>
242
+ <div class="text-sm text-gray-500">Approximate Location</div>
243
+ <div id="client-location" class="text-lg font-medium text-gray-800">{clientLocation}</div>
244
+ </div>
245
+ </div>
246
+ </div>
247
+ <div>
248
+ <h3 class="text-sm font-medium text-gray-500 mb-2">SERVER INFORMATION</h3>
249
+ <div class="flex items-center mb-3">
250
+ <i class="fas fa-server text-emerald-500 mr-3"></i>
251
+ <div>
252
+ <div class="text-sm text-gray-500">Server Location</div>
253
+ <div id="server-location" class="text-lg font-medium text-gray-800">{serverLocation}</div>
254
+ </div>
255
+ </div>
256
+ <div class="flex items-center">
257
+ <i class="fas fa-network-wired text-emerald-500 mr-3"></i>
258
+ <div>
259
+ <div class="text-sm text-gray-500">Test Server</div>
260
+ <div class="text-lg font-medium text-gray-800">huggingface.co</div>
261
+ </div>
262
+ </div>
263
+ </div>
264
+ </div>
265
+ </div>
266
+ </div>
267
  <!-- Results Graph -->
268
  <div class="bg-white rounded-xl shadow-lg overflow-hidden mb-8">
269
  <div class="p-6 md:p-8">
src/routes/+page.ts CHANGED
@@ -1,4 +1,5 @@
1
  import type { PageLoad } from './$types';
 
2
  export const ssr = false;
3
 
4
  export const load: PageLoad = ({ params }) => {
 
1
  import type { PageLoad } from './$types';
2
+
3
  export const ssr = false;
4
 
5
  export const load: PageLoad = ({ params }) => {
static/apple-touch-icon.png ADDED
static/favicon-96x96.png ADDED
static/favicon.ico ADDED
static/favicon.svg ADDED
static/site.webmanifest ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "Hugging Face Fast",
3
+ "short_name": "HF Fast",
4
+ "icons": [
5
+ {
6
+ "src": "/assets/web-app-manifest-192x192.png",
7
+ "sizes": "192x192",
8
+ "type": "image/png",
9
+ "purpose": "maskable"
10
+ },
11
+ {
12
+ "src": "/assets/web-app-manifest-512x512.png",
13
+ "sizes": "512x512",
14
+ "type": "image/png",
15
+ "purpose": "maskable"
16
+ }
17
+ ],
18
+ "theme_color": "#ffffff",
19
+ "background_color": "#ffffff",
20
+ "display": "standalone"
21
+ }
static/web-app-manifest-192x192.png ADDED
static/web-app-manifest-512x512.png ADDED