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
|