tokwatchr 0.4.0 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +9 -16
- package/package.json +5 -1
- package/src/download/ffmpeg.ts +18 -28
package/dist/index.mjs
CHANGED
|
@@ -391,21 +391,8 @@ async function downloadWithFfmpeg(options) {
|
|
|
391
391
|
let stderrBuffer = "";
|
|
392
392
|
let sizeBytes = 0;
|
|
393
393
|
let duration = 0;
|
|
394
|
-
const abortHandler = () => {
|
|
395
|
-
proc.kill("SIGTERM");
|
|
396
|
-
setTimeout(() => {
|
|
397
|
-
if (proc.exitCode === null) proc.kill("SIGKILL");
|
|
398
|
-
}, 15e3);
|
|
399
|
-
};
|
|
400
|
-
let aborted = false;
|
|
401
|
-
const onAbort = () => {
|
|
402
|
-
aborted = true;
|
|
403
|
-
abortHandler();
|
|
404
|
-
};
|
|
405
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
406
394
|
let firstDataTimer = setTimeout(() => {
|
|
407
395
|
firstDataTimer = null;
|
|
408
|
-
signal?.removeEventListener("abort", onAbort);
|
|
409
396
|
proc.kill("SIGTERM");
|
|
410
397
|
reject(new FfmpegError(`ffmpeg produced no output within ${timeout}ms — check the stream URL`));
|
|
411
398
|
}, timeout);
|
|
@@ -439,7 +426,14 @@ async function downloadWithFfmpeg(options) {
|
|
|
439
426
|
clearTimeout(firstDataTimer);
|
|
440
427
|
firstDataTimer = null;
|
|
441
428
|
}
|
|
442
|
-
|
|
429
|
+
if (err.name === "AbortError") {
|
|
430
|
+
resolve({
|
|
431
|
+
sizeBytes,
|
|
432
|
+
duration,
|
|
433
|
+
format: outputPath.endsWith(".mkv") ? "mkv" : "mp4"
|
|
434
|
+
});
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
443
437
|
reject(new FfmpegError(err.message));
|
|
444
438
|
});
|
|
445
439
|
proc.on("close", (code) => {
|
|
@@ -447,8 +441,7 @@ async function downloadWithFfmpeg(options) {
|
|
|
447
441
|
clearTimeout(firstDataTimer);
|
|
448
442
|
firstDataTimer = null;
|
|
449
443
|
}
|
|
450
|
-
signal?.
|
|
451
|
-
if (aborted) {
|
|
444
|
+
if (signal?.aborted) {
|
|
452
445
|
resolve({
|
|
453
446
|
sizeBytes,
|
|
454
447
|
duration,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tokwatchr",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"description": "Download TikTok livestreams. Given a username, download the livestream.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.mjs",
|
|
@@ -40,6 +40,10 @@
|
|
|
40
40
|
"stream"
|
|
41
41
|
],
|
|
42
42
|
"license": "MIT",
|
|
43
|
+
"repository": {
|
|
44
|
+
"type": "git",
|
|
45
|
+
"url": "git+https://github.com/zfadhli/tokwatchr.git"
|
|
46
|
+
},
|
|
43
47
|
"devDependencies": {
|
|
44
48
|
"@biomejs/biome": "^2.4.16",
|
|
45
49
|
"@types/bun": "latest",
|
package/src/download/ffmpeg.ts
CHANGED
|
@@ -122,35 +122,12 @@ export async function downloadWithFfmpeg(
|
|
|
122
122
|
let sizeBytes = 0;
|
|
123
123
|
let duration = 0;
|
|
124
124
|
|
|
125
|
-
const abortHandler = () => {
|
|
126
|
-
// SIGTERM tells ffmpeg to flush its output (moov atom, etc.)
|
|
127
|
-
// and exit cleanly. This produces a playable file even when
|
|
128
|
-
// stopped mid-recording.
|
|
129
|
-
proc.kill("SIGTERM");
|
|
130
|
-
// Safety net: if ffmpeg doesn't respond, force-kill after 15s.
|
|
131
|
-
// The close handler will still fire and resolve with partial data.
|
|
132
|
-
setTimeout(() => {
|
|
133
|
-
if (proc.exitCode === null) {
|
|
134
|
-
proc.kill("SIGKILL");
|
|
135
|
-
}
|
|
136
|
-
}, 15_000);
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
let aborted = false;
|
|
140
|
-
const onAbort = () => {
|
|
141
|
-
aborted = true;
|
|
142
|
-
abortHandler();
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
146
|
-
|
|
147
125
|
// Startup timeout — if ffmpeg produces no stderr output within
|
|
148
126
|
// `timeout` ms, it likely stalled on a bad URL. Kill it so the
|
|
149
127
|
// caller doesn't hang indefinitely.
|
|
150
128
|
let firstDataTimer: ReturnType<typeof setTimeout> | null = setTimeout(
|
|
151
129
|
() => {
|
|
152
130
|
firstDataTimer = null;
|
|
153
|
-
signal?.removeEventListener("abort", onAbort);
|
|
154
131
|
proc.kill("SIGTERM");
|
|
155
132
|
reject(
|
|
156
133
|
new FfmpegError(
|
|
@@ -200,7 +177,18 @@ export async function downloadWithFfmpeg(
|
|
|
200
177
|
clearTimeout(firstDataTimer);
|
|
201
178
|
firstDataTimer = null;
|
|
202
179
|
}
|
|
203
|
-
signal
|
|
180
|
+
// spawn's signal option emits AbortError when the signal
|
|
181
|
+
// is already aborted at spawn time or fires mid-flight.
|
|
182
|
+
// Resolve with partial data instead of rejecting — the
|
|
183
|
+
// close handler will also fire and see signal?.aborted.
|
|
184
|
+
if (err.name === "AbortError") {
|
|
185
|
+
resolve({
|
|
186
|
+
sizeBytes,
|
|
187
|
+
duration,
|
|
188
|
+
format: outputPath.endsWith(".mkv") ? "mkv" : "mp4",
|
|
189
|
+
});
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
204
192
|
reject(new FfmpegError(err.message));
|
|
205
193
|
});
|
|
206
194
|
|
|
@@ -209,12 +197,14 @@ export async function downloadWithFfmpeg(
|
|
|
209
197
|
clearTimeout(firstDataTimer);
|
|
210
198
|
firstDataTimer = null;
|
|
211
199
|
}
|
|
212
|
-
signal?.removeEventListener("abort", onAbort);
|
|
213
200
|
|
|
214
201
|
// When the user aborted, resolve with whatever we got.
|
|
215
|
-
//
|
|
216
|
-
// its output and exit cleanly
|
|
217
|
-
|
|
202
|
+
// spawn's signal option sends SIGTERM, which tells ffmpeg
|
|
203
|
+
// to flush its output and exit cleanly.
|
|
204
|
+
// Use signal?.aborted directly instead of a listener flag
|
|
205
|
+
// to avoid a race between listener registration order and
|
|
206
|
+
// the async close event.
|
|
207
|
+
if (signal?.aborted) {
|
|
218
208
|
resolve({
|
|
219
209
|
sizeBytes,
|
|
220
210
|
duration,
|