@torrent-tv/proxy 2.9.102 → 2.9.103
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/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
## 2.9.103
|
|
2
|
+
|
|
3
|
+
- **Fix**: Playback worked in neither 2.9.101 nor 2.9.102. Both cold-start estimates keep a window of recent samples, and the constant naming that window was used twice and declared nowhere. The session-create one runs on every new session, so `POST /api/transcode-sessions` answered 500 to every viewer and the browser then reported the first segment missing. Field session 2026-08-05: the plan succeeded in 5858 ms, the session request failed 47 ms later, the data channel closed 16 ms after that.
|
|
4
|
+
- **Fix**: The fallback read path threw the same way. `createReadStream` passed a `windowBytes` its own signature never accepted — a reference to nothing, which in a module is an error, not an undefined. It is the path taken for a source with no shared piece pool, so it had never run on a host where it would have been noticed.
|
|
5
|
+
- **Fix**: A failed session no longer leaves its directory behind. It was created before the probe and the keyframe index, both of which can fail, and nothing tracks or sweeps a directory whose session was never registered.
|
|
6
|
+
- **New**: The transcode-session route says why it failed, on the proxy's own log and with the stack. It caught, answered 500 and stayed silent, so the log carried only the data-channel layer's bare `→ 500`: the cause of the defect above had to be recovered by replaying the request against the live proxy.
|
|
7
|
+
- **Chore**: The proxy has a linter. It had none, and the rule for an undeclared name catches this whole class outright — it found the second occurrence above on its first run. Biome, `npm run lint`, limited to the correctness rules that describe real faults rather than style.
|
|
8
|
+
|
|
1
9
|
## 2.9.102
|
|
2
10
|
|
|
3
11
|
- **New**: The playback plan also reports what this host takes to CREATE a session — median of the last eight, 116-843 ms depending on whether the keyframe index is already in hand. It is the second term of the browser's end-to-end estimate, which is being rebuilt as a sum over the stages that have not happened yet rather than a choice between figures that each describe only one of them (`research/playback-eta-2026-08-05.md`).
|
package/biome.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://biomejs.dev/schemas/2.5.7/schema.json",
|
|
3
|
+
"files": {
|
|
4
|
+
"includes": ["**/*.js", "!**/node_modules/**"]
|
|
5
|
+
},
|
|
6
|
+
"formatter": { "enabled": false },
|
|
7
|
+
"assist": { "enabled": false },
|
|
8
|
+
"linter": {
|
|
9
|
+
"enabled": true,
|
|
10
|
+
"rules": {
|
|
11
|
+
"preset": "none",
|
|
12
|
+
"correctness": {
|
|
13
|
+
"noUndeclaredVariables": "error",
|
|
14
|
+
"noUnusedImports": "error",
|
|
15
|
+
"noUnusedVariables": "error",
|
|
16
|
+
"noInvalidUseBeforeDeclaration": "error",
|
|
17
|
+
"noPrivateImports": "error"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@torrent-tv/proxy",
|
|
3
|
-
"version": "2.9.
|
|
3
|
+
"version": "2.9.103",
|
|
4
4
|
"description": "Torrent proxy client that exposes webseed-like HTTP stream endpoint.",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
6
|
"publishConfig": {
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
"major": "npm whoami && npm version major && npm publish && git push --follow-tags",
|
|
17
17
|
"start": "node ./bin/cli.js",
|
|
18
18
|
"dev": "node --inspect=0 --experimental-network-inspection ./bin/cli.js",
|
|
19
|
-
"test": "node --test"
|
|
19
|
+
"test": "node --test",
|
|
20
|
+
"lint": "biome lint ."
|
|
20
21
|
},
|
|
21
22
|
"dependencies": {
|
|
22
23
|
"@fastify/cors": "^11.2.0",
|
|
@@ -34,5 +35,8 @@
|
|
|
34
35
|
"webtorrent": "2.8.5",
|
|
35
36
|
"werift": "^0.24.2",
|
|
36
37
|
"ws": "^8.18.2"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@biomejs/biome": "^2.5.7"
|
|
37
41
|
}
|
|
38
42
|
}
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
* @returns {Promise<void>}
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
import { logger } from "../../../utils/logger.js";
|
|
13
|
+
|
|
12
14
|
/**
|
|
13
15
|
* Extract a plain object from the request body, guarding against
|
|
14
16
|
* non-object payloads (arrays, primitives, null).
|
|
@@ -77,6 +79,17 @@ export async function handleApiTranscodeSessionsPost(req, reply, { hlsSessionMan
|
|
|
77
79
|
return reply.code(409).send({ error: error.message });
|
|
78
80
|
}
|
|
79
81
|
const message = error instanceof Error ? error.message : String(error);
|
|
82
|
+
// Say why on the proxy's own log, not only in the answer. This route
|
|
83
|
+
// answered 500 for every viewer of proxy 2.9.101-2.9.102 (an undeclared
|
|
84
|
+
// constant) and the addon log carried nothing but the data-channel layer's
|
|
85
|
+
// bare "→ 500": the cause had to be recovered by replaying the request
|
|
86
|
+
// against the live proxy. The stack is worth the two lines it costs — a
|
|
87
|
+
// programming error here is invisible to the viewer, who only sees that
|
|
88
|
+
// nothing plays.
|
|
89
|
+
logger.error(
|
|
90
|
+
`transcode-sessions: ${sourceKey}:${fileIndex} failed to prepare: ${message}\n` +
|
|
91
|
+
`${error instanceof Error ? (error.stack ?? "") : ""}`
|
|
92
|
+
);
|
|
80
93
|
return reply.code(500).send({ error: `Failed to prepare transcode session: ${message}` });
|
|
81
94
|
}
|
|
82
95
|
}
|
|
@@ -181,6 +181,13 @@ const LINK_LOW_BUFFER_SEC = 10;
|
|
|
181
181
|
// Observed produced bitrate: average over this many recently completed
|
|
182
182
|
// segments (the newest file on disk may still be written and is excluded).
|
|
183
183
|
const LINK_OBSERVED_SEGMENTS = 5;
|
|
184
|
+
// How many recent runs the two cold-start estimates keep. Both the
|
|
185
|
+
// session-create time and the first-segment time are reported to the browser as
|
|
186
|
+
// the median of this many samples, so it has to be long enough that one slow run
|
|
187
|
+
// does not move the figure and short enough that the estimate still follows the
|
|
188
|
+
// host: a proxy whose swarm has warmed up, or which has just picked up a second
|
|
189
|
+
// viewer, should stop quoting the numbers from ten minutes ago.
|
|
190
|
+
const FIRST_SEGMENT_SAMPLES = 20;
|
|
184
191
|
const MICROSECONDS_PER_SECOND = 1_000_000;
|
|
185
192
|
const PROGRESS_LOG_INTERVAL_MS = 5_000;
|
|
186
193
|
// Read segment files in large blocks so the body is delivered to the data
|
|
@@ -990,7 +997,6 @@ export class HlsSessionManager {
|
|
|
990
997
|
const sessionId = randomUUID();
|
|
991
998
|
const createEntryMs = Date.now();
|
|
992
999
|
const sessionDir = createSessionDirPath(sessionId);
|
|
993
|
-
await mkdir(sessionDir, { recursive: true });
|
|
994
1000
|
const inputUrl = new URL("/stream", `${this.localBaseUrl}/`);
|
|
995
1001
|
inputUrl.searchParams.set("sourceKey", sourceKey);
|
|
996
1002
|
inputUrl.searchParams.set("fileIndex", String(fileIndex));
|
|
@@ -1176,6 +1182,14 @@ export class HlsSessionManager {
|
|
|
1176
1182
|
const encodeWidth = encodeBudget?.width ?? normalizedTargetWidth;
|
|
1177
1183
|
const encodeHeight = encodeBudget?.height ?? normalizedTargetHeight;
|
|
1178
1184
|
|
|
1185
|
+
// Only now, when nothing above can still throw. Everything from the probe
|
|
1186
|
+
// to the keyframe index used to run with the directory already made, so a
|
|
1187
|
+
// failure between the two left it behind: nothing tracks a directory whose
|
|
1188
|
+
// session was never registered, and no sweep looks for one. Proxy
|
|
1189
|
+
// 2.9.101-2.9.102 failed here on every single request and the leftovers
|
|
1190
|
+
// were the only trace of it on disk.
|
|
1191
|
+
await mkdir(sessionDir, { recursive: true });
|
|
1192
|
+
|
|
1179
1193
|
const session = {
|
|
1180
1194
|
id: sessionId,
|
|
1181
1195
|
sourceMapKey,
|
|
@@ -231,7 +231,7 @@ export class TorrentWorkerClient {
|
|
|
231
231
|
* @param {{ sourceKey: string, fileIndex: number, start?: number | null, end?: number | null, windowBytes?: number }} params
|
|
232
232
|
* @returns {ReadableStream<Uint8Array>}
|
|
233
233
|
*/
|
|
234
|
-
createReadStream({ sourceKey, fileIndex, start = null, end = null }) {
|
|
234
|
+
createReadStream({ sourceKey, fileIndex, start = null, end = null, windowBytes }) {
|
|
235
235
|
// Same id sequence as commands — see `nextId` in `channel.js`.
|
|
236
236
|
const readId = this.#caller.nextId();
|
|
237
237
|
const receive = createReceiveStream({
|
|
@@ -407,7 +407,8 @@ export class TorrentWorkerClient {
|
|
|
407
407
|
sourceKey,
|
|
408
408
|
fileIndex: file.index,
|
|
409
409
|
start: options.start ?? null,
|
|
410
|
-
end: options.end ?? null
|
|
410
|
+
end: options.end ?? null,
|
|
411
|
+
windowBytes: options.windowBytes
|
|
411
412
|
})
|
|
412
413
|
);
|
|
413
414
|
},
|
package/test/webrtc-shim.test.js
CHANGED