Skip to content

Commit 8086620

Browse files
committed
init
0 parents  commit 8086620

File tree

4 files changed

+304
-0
lines changed

4 files changed

+304
-0
lines changed

.gitignore

+157
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
lerna-debug.log*
8+
.pnpm-debug.log*
9+
10+
# Diagnostic reports (https://nodejs.org/api/report.html)
11+
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
12+
13+
# Runtime data
14+
pids
15+
*.pid
16+
*.seed
17+
*.pid.lock
18+
19+
# Directory for instrumented libs generated by jscoverage/JSCover
20+
lib-cov
21+
22+
# Coverage directory used by tools like istanbul
23+
coverage
24+
*.lcov
25+
26+
# nyc test coverage
27+
.nyc_output
28+
29+
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30+
.grunt
31+
32+
# Bower dependency directory (https://bower.io/)
33+
bower_components
34+
35+
# node-waf configuration
36+
.lock-wscript
37+
38+
# Compiled binary addons (https://nodejs.org/api/addons.html)
39+
build/Release
40+
41+
# Dependency directories
42+
node_modules/
43+
jspm_packages/
44+
45+
# Snowpack dependency directory (https://snowpack.dev/)
46+
web_modules/
47+
48+
# TypeScript cache
49+
*.tsbuildinfo
50+
51+
# Optional npm cache directory
52+
.npm
53+
54+
# Optional eslint cache
55+
.eslintcache
56+
57+
# Optional stylelint cache
58+
.stylelintcache
59+
60+
# Microbundle cache
61+
.rpt2_cache/
62+
.rts2_cache_cjs/
63+
.rts2_cache_es/
64+
.rts2_cache_umd/
65+
66+
# Optional REPL history
67+
.node_repl_history
68+
69+
# Output of 'npm pack'
70+
*.tgz
71+
72+
# Yarn Integrity file
73+
.yarn-integrity
74+
75+
# dotenv environment variable files
76+
.env
77+
.env.development.local
78+
.env.test.local
79+
.env.production.local
80+
.env.local
81+
82+
# parcel-bundler cache (https://parceljs.org/)
83+
.cache
84+
.parcel-cache
85+
86+
# Next.js build output
87+
.next
88+
out
89+
90+
# Nuxt.js build / generate output
91+
.nuxt
92+
dist
93+
94+
# Gatsby files
95+
.cache/
96+
# Comment in the public line in if your project uses Gatsby and not Next.js
97+
# https://nextjs.org/blog/next-9-1#public-directory-support
98+
# public
99+
100+
# vuepress build output
101+
.vuepress/dist
102+
103+
# vuepress v2.x temp and cache directory
104+
.temp
105+
.cache
106+
107+
# Docusaurus cache and generated files
108+
.docusaurus
109+
110+
# Serverless directories
111+
.serverless/
112+
113+
# FuseBox cache
114+
.fusebox/
115+
116+
# DynamoDB Local files
117+
.dynamodb/
118+
119+
# TernJS port file
120+
.tern-port
121+
122+
# Stores VSCode versions used for testing VSCode extensions
123+
.vscode-test
124+
125+
# yarn v2
126+
.yarn/cache
127+
.yarn/unplugged
128+
.yarn/build-state.yml
129+
.yarn/install-state.gz
130+
.pnp.*
131+
132+
# General
133+
.DS_Store
134+
.AppleDouble
135+
.LSOverride
136+
137+
# Icon must end with two \r
138+
Icon
139+
140+
# Thumbnails
141+
._*
142+
143+
# Files that might appear in the root of a volume
144+
.DocumentRevisions-V100
145+
.fseventsd
146+
.Spotlight-V100
147+
.TemporaryItems
148+
.Trashes
149+
.VolumeIcon.icns
150+
.com.apple.timemachine.donotpresent
151+
152+
# Directories potentially created on remote AFP share
153+
.AppleDB
154+
.AppleDesktop
155+
Network Trash Folder
156+
Temporary Items
157+
.apdisk

.npmrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package-lock=false

index.js

+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
#!/usr/bin/env node
2+
import { load } from 'cheerio'
3+
import axios from 'axios'
4+
import { parse } from 'acorn';
5+
import { simple } from 'acorn-walk'
6+
import { performance } from 'node:perf_hooks'
7+
import ms from 'ms'
8+
import chalk from 'chalk'
9+
import clear from 'console-clear';
10+
11+
async function parseToken(scriptPath) {
12+
const { data: script } = await axios.get(scriptPath, { responseType: 'text' });
13+
const result = parse(script, {
14+
allowHashBang: true,
15+
ecmaVersion: 2022
16+
});
17+
18+
return new Promise((resolve) => {
19+
let token = null;
20+
simple(result, {
21+
Property(node) {
22+
if (token != null) return;
23+
if (node.key.name === "token") {
24+
token = node.value.value;
25+
}
26+
},
27+
});
28+
resolve(token)
29+
})
30+
}
31+
32+
33+
34+
clear(true)
35+
console.log(chalk.red.bold("FAST.COM"))
36+
console.log(chalk.green('Fetch script'));
37+
const { data } = await axios.get("https://fast.com/", { responseType: 'text' });
38+
const $ = load(data);
39+
const token = await parseToken(new URL($("script[src]").first().attr('src'), "https://fast.com/"));
40+
41+
clear(true)
42+
console.log(chalk.red.bold("FAST.COM"))
43+
console.log(chalk.green('Parse token'));
44+
const { data: { client: _client, targets } } = await axios.get(`https://api.fast.com/netflix/speedtest/v2?https=true&token=${token}&urlCount=5`);
45+
46+
let bytes = 0;
47+
const avg = [];
48+
49+
const controller = new AbortController();
50+
51+
let startedAt = Date.now();
52+
let elapsed = Date.now();
53+
54+
const responseTimeSamples = [];
55+
56+
57+
58+
const spinner = '◐◓◑◒'.split('')
59+
let tick = 0;
60+
61+
let lastReport = {};
62+
63+
await Promise.all(
64+
targets.map(async ({ url }) => {
65+
try {
66+
const { data: stream } = await axios.get(url, { responseType: 'stream', signal: controller.signal });
67+
let responseTime = performance.now();
68+
69+
stream.on('data', buffer => {
70+
71+
bytes += buffer.length;
72+
73+
responseTime = performance.now() - responseTime;
74+
responseTimeSamples.push(responseTime)
75+
responseTime = performance.now()
76+
77+
if (Date.now() - startedAt > 100) {
78+
tick++;
79+
avg.push(bytes * 8 * 10);
80+
const averageBits = avg.reduce((a, b) => a + b, 0) / avg.length;
81+
const latency = ms(Math.round(responseTimeSamples.reduce((a, b) => a + b) / responseTimeSamples.length), { long: true })
82+
const spin = offset => chalk.dim(`${spinner[(tick + offset) % spinner.length]}`);
83+
clear(true)
84+
85+
const timeElapsed = ms(Date.now() - elapsed);
86+
87+
console.log(chalk.red.bold("FAST.COM"))
88+
console.log(spin(2) + chalk.yellow(' Elapsed\t') + timeElapsed);
89+
console.log(spin(3) + chalk.yellow(' Latency\t') + latency);
90+
console.log(spin(4) + chalk.yellow(' Avg. Speed\t') + Math.round(averageBits / 1000000) + " Mbps ");
91+
92+
lastReport = {
93+
averageBits,
94+
latency,
95+
timeElapsed
96+
}
97+
98+
bytes = 0;
99+
startedAt += 100;
100+
}
101+
});
102+
stream.on('end', () => {
103+
controller.abort();
104+
const {timeElapsed, latency, averageBits} = lastReport;
105+
const spin = () => chalk.green("✔");
106+
clear(true)
107+
console.log(chalk.red.bold("FAST.COM"))
108+
console.log(spin(2) + chalk.green(' Elapsed\t') + timeElapsed);
109+
console.log(spin(3) + chalk.green(' Latency\t') + latency);
110+
console.log(spin(4) + chalk.green(' Avg. Speed\t') + Math.round(averageBits / 1000000) + " Mbps ");
111+
console.log('')
112+
console.log(chalk.green.bold('Your internet speed is ' + Math.round(averageBits / 1000000) + " Mbps "))
113+
console.log('')
114+
})
115+
} catch(e) {
116+
if (!axios.isAxiosError(e)) {
117+
throw e;
118+
}
119+
}
120+
})
121+
)
122+

package.json

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "fast.com",
3+
"version": "1.0.6",
4+
"description": "A fast.com cli",
5+
"bin": "./index.js",
6+
"type": "module",
7+
"main": "index.js",
8+
"keywords": [
9+
"fast",
10+
"speedtest"
11+
],
12+
"author": "Seanghay Yath",
13+
"repository": "seanghay/netflix-fast-cli",
14+
"license": "Apache-2.0",
15+
"dependencies": {
16+
"acorn": "^8.8.0",
17+
"acorn-walk": "^8.2.0",
18+
"axios": "^1.1.2",
19+
"chalk": "^5.1.0",
20+
"cheerio": "^1.0.0-rc.12",
21+
"console-clear": "^1.1.1",
22+
"ms": "^2.1.3"
23+
}
24+
}

0 commit comments

Comments
 (0)