@torrent-tv/proxy 2.89.9 → 2.89.11
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.89.11
|
|
2
|
+
|
|
3
|
+
- **Fix**: A failed encoder run says what the source holds again. The line `this run asked for …, and the source holds N video, N audio, N subtitle` was meant to tell an output with no stream (the source's fault) from a track index past the end (ours), but every field failure printed `what the source holds was not recorded`: a session takes its media info from the playback planner's cache, and the planner left the stream counts out of it although it parses the same ffmpeg banner. They are cached with the rest now.
|
|
4
|
+
|
|
5
|
+
## 2.89.10
|
|
6
|
+
|
|
7
|
+
- **Fix**: An audio track warm-up that is still being made answers `503` with `warming: true`. That answer means the track and how it is produced are recorded for the viewer; a `503` without it is a preparation that failed and recorded nothing. Until now the two were the same status, so a page restating its soundtrack after a reconnect could not tell whether the proxy had kept it.
|
|
8
|
+
|
|
1
9
|
## 2.89.9
|
|
2
10
|
|
|
3
11
|
- **Fix**: The delivery probe judges a connection by how old the newest probe the browser has seen is, not by how long that probe took to arrive. The time it took is fixed once it has arrived, so on the wedge of 2026-09-28 it stayed 9 ms for a whole minute while the browser saw nothing after probe 1464, and every line read `flowing`; on idle healthy connections its 3 ms allowance left out the browser's own handling of the message, and `association-stopped` was printed for connections that delivered every probe. The age's allowance now also counts the probe interval, which `probeWedgeIsCertain` already counted. A real wedge is now named within about a second and reaches the packet witness.
|
|
@@ -94,7 +94,7 @@ soundtracks the viewer can choose from.
|
|
|
94
94
|
| `POST /api/transcode-sessions/:id/fragment-far` | Diagnostic fragment and buffer positions; does not change encoding. Best effort, success `204`. |
|
|
95
95
|
| `POST /api/transcode-sessions/:id/release` | `{ consumerId, reason }` releases this viewer's session assignment. |
|
|
96
96
|
| `GET /transcode/:id/:fileName` | HLS playlist, init, or media segment. A file that is still being produced is answered with retryable `503`, not `202`. |
|
|
97
|
-
| `GET /transcode/:id/v/:height/warm` and `/transcode/:id/a/:track/warm` | Prepare the requested video height or audio track at a position. `204` means ready. Audio warm-up `404` means the proxy does not support this operation for that session. Audio warm-up takes an optional `transcode=0\|1`: whether the browser cannot play THAT track's codec as it is. The proxy decides the track's mode by the rule used when a file is opened; without the parameter it assumes the browser needs what it needed for its current track. |
|
|
97
|
+
| `GET /transcode/:id/v/:height/warm` and `/transcode/:id/a/:track/warm` | Prepare the requested video height or audio track at a position. `204` means ready. Audio warm-up `404` means the proxy does not support this operation for that session. Audio warm-up takes an optional `transcode=0\|1`: whether the browser cannot play THAT track's codec as it is. The proxy decides the track's mode by the rule used when a file is opened; without the parameter it assumes the browser needs what it needed for its current track. Audio warm-up `503` with `{ warming: true }` means the track and its mode are recorded for this viewer and its segment is still being made; any other `503`, and `500`, recorded nothing. |
|
|
98
98
|
| `GET /stream?sourceKey=…&fileIndex=N` | Direct file bytes with HTTP range support. |
|
|
99
99
|
|
|
100
100
|
### Playback readiness forecast
|
package/package.json
CHANGED
|
@@ -90,6 +90,12 @@ export async function handleTranscodeAudioWarmGet(req, reply, { renditions, serv
|
|
|
90
90
|
}
|
|
91
91
|
// Still being produced. The caller may switch anyway — it will wait where it
|
|
92
92
|
// would have waited before — or ask again.
|
|
93
|
+
//
|
|
94
|
+
// `warming: true` says the track and how it is produced ARE recorded for this
|
|
95
|
+
// viewer — `prepareAudioTrack` records them before the wait — which the
|
|
96
|
+
// status alone cannot say: the 503 above is a preparation that recorded
|
|
97
|
+
// nothing. A page restating its soundtrack after a reconnect needs to know
|
|
98
|
+
// which of the two it got.
|
|
93
99
|
reply.header("Retry-After", "1");
|
|
94
|
-
return reply.code(503).send({ error: "The audio track is still warming up." });
|
|
100
|
+
return reply.code(503).send({ error: "The audio track is still warming up.", warming: true });
|
|
95
101
|
}
|
|
@@ -16,7 +16,8 @@ import {
|
|
|
16
16
|
parseFfmpegBitDepth,
|
|
17
17
|
parseFfmpegBitrateKbps,
|
|
18
18
|
parseFfmpegVideoFps,
|
|
19
|
-
parseFfmpegHdr
|
|
19
|
+
parseFfmpegHdr,
|
|
20
|
+
parseFfmpegStreamCounts
|
|
20
21
|
} from "./ffmpeg-banner.js";
|
|
21
22
|
|
|
22
23
|
/** Audio codecs that browsers can decode natively without transcoding. */
|
|
@@ -912,7 +913,12 @@ export function createPlaybackPlanner({
|
|
|
912
913
|
fps: videoFacts.fps,
|
|
913
914
|
startTime: parseFfmpegStartTimeSeconds(probe.stderr),
|
|
914
915
|
isHdr: videoFacts.isHdr,
|
|
915
|
-
bitDepth: videoFacts.bitDepth
|
|
916
|
+
bitDepth: videoFacts.bitDepth,
|
|
917
|
+
// What the source holds, from this same banner. A failed run reads it
|
|
918
|
+
// to say whose fault an output with no stream is, and a session takes
|
|
919
|
+
// its media info from this cache without probing again — so a field
|
|
920
|
+
// left out here was "not recorded" on every failure (field 2026-09-29).
|
|
921
|
+
streamCounts: parseFfmpegStreamCounts(probe.stderr)
|
|
916
922
|
});
|
|
917
923
|
// Warm the file-body start for the transcode session that follows.
|
|
918
924
|
// Fire-and-forget: never delays the plan response.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
|
|
4
|
+
import { handleTranscodeAudioWarmGet } from "../routes/transcode/audio-warm/get.js";
|
|
5
|
+
|
|
6
|
+
// A page restating its soundtrack after a reconnect has to know whether the
|
|
7
|
+
// proxy RECORDED it. Both answers below are 503; only one of them recorded.
|
|
8
|
+
|
|
9
|
+
function reply() {
|
|
10
|
+
return {
|
|
11
|
+
statusCode: 0,
|
|
12
|
+
payload: undefined,
|
|
13
|
+
header() {
|
|
14
|
+
return this;
|
|
15
|
+
},
|
|
16
|
+
code(status) {
|
|
17
|
+
this.statusCode = status;
|
|
18
|
+
return this;
|
|
19
|
+
},
|
|
20
|
+
send(payload) {
|
|
21
|
+
this.payload = payload;
|
|
22
|
+
return this;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const request = {
|
|
28
|
+
params: { sessionId: "base", track: "0" },
|
|
29
|
+
query: { position: "1641.2", consumer: "viewer-1", transcode: "1" }
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
test("a track still being made says it was recorded", async () => {
|
|
33
|
+
let preparedFor = null;
|
|
34
|
+
const answer = await handleTranscodeAudioWarmGet(request, reply(), {
|
|
35
|
+
renditions: {
|
|
36
|
+
prepareAudioTrack: async (...args) => {
|
|
37
|
+
preparedFor = args;
|
|
38
|
+
return { sessionId: "rendition", fileName: "segment-00268.mp4" };
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
serving: {
|
|
42
|
+
seekEpoch: () => 0,
|
|
43
|
+
// Anything but a file or a failure is "not made yet".
|
|
44
|
+
getFileStream: async () => ({ kind: "superseded" })
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
assert.deepEqual(preparedFor, ["base", 0, 1641.2, "viewer-1", false]);
|
|
48
|
+
assert.equal(answer.statusCode, 503);
|
|
49
|
+
assert.equal(answer.payload.warming, true);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("a preparation that failed says nothing was recorded", async () => {
|
|
53
|
+
const answer = await handleTranscodeAudioWarmGet(request, reply(), {
|
|
54
|
+
renditions: {
|
|
55
|
+
prepareAudioTrack: async () => {
|
|
56
|
+
throw new Error("no encoder");
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
serving: {}
|
|
60
|
+
});
|
|
61
|
+
assert.equal(answer.statusCode, 503);
|
|
62
|
+
assert.equal(answer.payload.warming, undefined);
|
|
63
|
+
});
|