Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
feat: enhance bandwidth test with client and server information display
Browse files- src/app.html +15 -9
- src/lib/icao.ts +236 -0
- src/lib/index.ts +50 -19
- src/routes/+layout.svelte +1 -1
- src/routes/+page.svelte +71 -5
- src/routes/+page.ts +1 -0
- static/apple-touch-icon.png +0 -0
- static/favicon-96x96.png +0 -0
- static/favicon.ico +0 -0
- static/favicon.svg +0 -0
- static/site.webmanifest +21 -0
- static/web-app-manifest-192x192.png +0 -0
- static/web-app-manifest-512x512.png +0 -0
src/app.html
CHANGED
@@ -1,12 +1,18 @@
|
|
1 |
<!doctype html>
|
2 |
<html lang="en">
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
<
|
10 |
-
|
11 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
|
|
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 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
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,
|
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
|
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
![]() |