Skip to content

Commit a9ddafb

Browse files
committed
feat: allows skipping specific steps
1 parent f2031c4 commit a9ddafb

2 files changed

Lines changed: 119 additions & 66 deletions

File tree

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,26 @@ video-link-debugger test --link https://example.com/video.mp4
5353

5454
- `test` — Tests a video link and reports link information, network timings, seek behavior, and single- vs. multi-connection download speed.
5555

56+
#### `test` flags
57+
58+
By default `test` runs every phase. Pass any of these to skip a phase:
59+
60+
| Flag | Short | Skips |
61+
| --- | --- | --- |
62+
| `--skip-timings` | `-T` | DNS / TCP / TLS / TTFB measurement and the **Network Timings** table |
63+
| `--skip-seek` | `-S` | Random seek probes and the **Seek Results** table |
64+
| `--skip-download` | `-D` | Single- and multi-connection downloads and the **Download Comparison** table |
65+
66+
Link information is always printed.
67+
68+
```bash
69+
video-link-debugger test https://example.com/video.mp4 # everything
70+
video-link-debugger test https://example.com/video.mp4 -D # no downloads
71+
video-link-debugger test https://example.com/video.mp4 -T -S # only download tests
72+
```
73+
74+
> Short flags can't be bundled — write `-T -S`, not `-TS`.
75+
5676
## Development
5777

5878
```bash

src/commands/test.ts

Lines changed: 99 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -66,89 +66,122 @@ export default defineCommand({
6666
description: "Tests a video link and simulates start, seek and buffering.",
6767
options: {
6868
link: option(z.url().optional(), { description: "Link to test", short: "l" }),
69+
skipTimings: option(z.boolean().default(false), {
70+
description: "Skip network timing measurements",
71+
short: "T",
72+
argumentKind: "flag",
73+
}),
74+
skipSeek: option(z.boolean().default(false), {
75+
description: "Skip random seek tests",
76+
short: "S",
77+
argumentKind: "flag",
78+
}),
79+
skipDownload: option(z.boolean().default(false), {
80+
description: "Skip single- and multi-connection download tests",
81+
short: "D",
82+
argumentKind: "flag",
83+
}),
6984
},
7085
handler: async ({ flags, positional }) => {
7186
const link = z.url().parse(positional[0] ?? flags.link);
87+
const { skipTimings, skipSeek, skipDownload } = flags;
7288

7389
const linkInfo = await getLinkInformation(link);
7490

75-
const renderer = await createCliRenderer({ exitOnCtrlC: true });
76-
77-
const progressBox = new BoxRenderable(renderer, {
78-
borderStyle: "rounded",
79-
padding: 1,
80-
title: "Measuring",
81-
});
82-
const rows = new Map<TimingPhase, TextRenderable>();
83-
for (const { key, label } of PHASES) {
84-
const row = new TextRenderable(renderer, {
85-
content: `⋯ ${label}`,
86-
fg: "#888888",
91+
const needsRenderer = !skipTimings || !skipDownload;
92+
const renderer = needsRenderer
93+
? await createCliRenderer({ exitOnCtrlC: true })
94+
: null;
95+
96+
const results = new Map<TimingPhase, number>();
97+
if (!skipTimings && renderer) {
98+
const progressBox = new BoxRenderable(renderer, {
99+
borderStyle: "rounded",
100+
padding: 1,
101+
title: "Measuring",
102+
});
103+
const rows = new Map<TimingPhase, TextRenderable>();
104+
for (const { key, label } of PHASES) {
105+
const row = new TextRenderable(renderer, {
106+
content: `⋯ ${label}`,
107+
fg: "#888888",
108+
});
109+
rows.set(key, row);
110+
progressBox.add(row);
111+
}
112+
renderer.root.add(progressBox);
113+
114+
await getLinkTimings(link, undefined, (phase, ms) => {
115+
results.set(phase, ms);
116+
const row = rows.get(phase);
117+
if (!row) return;
118+
const label = PHASES.find((p) => p.key === phase)?.label ?? phase;
119+
row.content = `✓ ${label.padEnd(16)} ${ms.toFixed(2)} ms`;
120+
row.fg = "#00d787";
87121
});
88-
rows.set(key, row);
89-
progressBox.add(row);
90122
}
91-
renderer.root.add(progressBox);
92123

93-
const results = new Map<TimingPhase, number>();
94-
await getLinkTimings(link, undefined, (phase, ms) => {
95-
results.set(phase, ms);
96-
const row = rows.get(phase);
97-
if (!row) return;
98-
const label = PHASES.find((p) => p.key === phase)?.label ?? phase;
99-
row.content = `✓ ${label.padEnd(16)} ${ms.toFixed(2)} ms`;
100-
row.fg = "#00d787";
101-
});
102-
103-
const seekResults = await SeekRandomMultipleTimes(linkInfo, link, 5);
104-
105-
const single = makeProgressBox(renderer, "Downloading (single connection)");
106-
renderer.root.add(single.box);
107-
const singleResult = await downloadFull(link, {
108-
connections: 1,
109-
size: linkInfo.size ?? undefined,
110-
onProgress: single.update,
111-
});
112-
single.finish(singleResult ? "#00d787" : "#ff5f5f");
124+
const seekResults = skipSeek
125+
? null
126+
: await SeekRandomMultipleTimes(linkInfo, link, 5);
113127

128+
let singleResult: DownloadResult | null = null;
114129
let multiResult: DownloadResult | null = null;
115130
const canMulti = !!linkInfo.size && linkInfo.acceptsRanges;
116-
if (canMulti) {
117-
const multi = makeProgressBox(
118-
renderer,
119-
`Downloading (${MULTI_CONNECTIONS} connections)`,
120-
);
121-
renderer.root.add(multi.box);
122-
multiResult = await downloadFull(link, {
123-
connections: MULTI_CONNECTIONS,
131+
132+
if (!skipDownload && renderer) {
133+
const single = makeProgressBox(renderer, "Downloading (single connection)");
134+
renderer.root.add(single.box);
135+
singleResult = await downloadFull(link, {
136+
connections: 1,
124137
size: linkInfo.size ?? undefined,
125-
onProgress: multi.update,
138+
onProgress: single.update,
126139
});
127-
multi.finish(multiResult ? "#00d787" : "#ff5f5f");
140+
single.finish(singleResult ? "#00d787" : "#ff5f5f");
141+
142+
if (canMulti) {
143+
const multi = makeProgressBox(
144+
renderer,
145+
`Downloading (${MULTI_CONNECTIONS} connections)`,
146+
);
147+
renderer.root.add(multi.box);
148+
multiResult = await downloadFull(link, {
149+
connections: MULTI_CONNECTIONS,
150+
size: linkInfo.size ?? undefined,
151+
onProgress: multi.update,
152+
});
153+
multi.finish(multiResult ? "#00d787" : "#ff5f5f");
154+
}
128155
}
129156

130-
renderer.destroy();
157+
renderer?.destroy();
131158

132159
console.log(renderTable("Link Information", linkInfoRows(linkInfo)));
133-
console.log(renderTable("Network Timings", timingRows(results)));
134-
console.log(
135-
renderMultiTable(
136-
"Seek Results",
137-
["#", "Status", "TTFB", "Receive", "Total"],
138-
seekRows(seekResults),
139-
),
140-
);
141-
console.log(
142-
renderMultiTable(
143-
"Download Comparison",
144-
["Mode", "Conns", "Time", "Bytes", "Speed"],
145-
downloadRows([
146-
{ label: "Single", result: singleResult },
147-
...(canMulti
148-
? [{ label: `Multi`, result: multiResult }]
149-
: []),
150-
]),
151-
),
152-
);
160+
if (!skipTimings) {
161+
console.log(renderTable("Network Timings", timingRows(results)));
162+
}
163+
if (!skipSeek && seekResults) {
164+
console.log(
165+
renderMultiTable(
166+
"Seek Results",
167+
["#", "Status", "TTFB", "Receive", "Total"],
168+
seekRows(seekResults),
169+
),
170+
);
171+
}
172+
if (!skipDownload) {
173+
console.log(
174+
renderMultiTable(
175+
"Download Comparison",
176+
["Mode", "Conns", "Time", "Bytes", "Speed"],
177+
downloadRows([
178+
{ label: "Single", result: singleResult },
179+
...(canMulti
180+
? [{ label: `Multi`, result: multiResult }]
181+
: []),
182+
]),
183+
),
184+
);
185+
}
153186
},
154187
});

0 commit comments

Comments
 (0)