@torrent-tv/proxy 2.89.6 → 2.89.8
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/AGENTS.md +32 -12
- package/CHANGELOG.md +2340 -2330
- package/assets/calibration/audio/NOTICE.md +19 -19
- package/assets/calibration/audio/manifest.json +98 -98
- package/biome.json +412 -412
- package/docs/browser-proxy-contract.md +155 -155
- package/docs/container-architecture.md +273 -273
- package/docs/encode-run-state.md +1 -1
- package/docs/playback-start-forecast.md +113 -98
- package/package.json +51 -50
- package/routes/api/sources/files/get.js +106 -106
- package/scripts/build-audio-samples.mjs +31 -31
- package/services/encode/EncodeRun.js +67 -15
- package/services/encode/EncodeRuns.js +19 -7
- package/services/encode/OutputOpening.js +784 -784
- package/services/encode/audio-calibration.js +67 -67
- package/services/encode/audio-work-rate.js +15 -15
- package/services/encode/encode-exit.js +2 -0
- package/services/encode/hwaccel.js +1894 -1894
- package/services/encode/output/index.js +24 -24
- package/services/encode/piece-completeness.js +60 -2
- package/services/encode/run-command.js +757 -755
- package/services/encode/segment-formats/index.js +1 -0
- package/services/encode/segment-formats/mp4-boxes.js +8 -1
- package/services/media/ContainerOrchestrator.js +202 -202
- package/services/media/KeyframeTables.js +356 -356
- package/services/media/SubtitleCues.js +592 -592
- package/services/media/audio-inventory.js +299 -299
- package/services/media/container/AviContainer.js +268 -268
- package/services/media/container/Container.js +632 -632
- package/services/media/container/ContainerFactory.js +38 -38
- package/services/media/container/MatroskaContainer.js +1031 -1031
- package/services/media/container/Mp4Container.js +1354 -1354
- package/services/media/container/SubtitleFileContainer.js +339 -339
- package/services/media/container/ebml-stream.js +174 -174
- package/services/media/container/matroska-clusters.js +1165 -1165
- package/services/media/container/unavailable.js +77 -77
- package/services/media/tracks/ContainerTrack.js +84 -84
- package/services/media/tracks/TextSubtitleTrack.js +295 -295
- package/services/storage/piece-store/shared-piece-store.js +1937 -1937
- package/services/storage/segment-store/SegmentStore.js +1294 -1294
- package/services/torrent/Contents.js +382 -382
- package/services/torrent/episode-naming.js +182 -182
- package/services/torrent/torrent-pool.js +2688 -2688
- package/services/torrent/worker/client.js +836 -836
- package/services/torrent/worker/held-bytes.js +231 -231
- package/services/torrent/worker/worker.js +1137 -1137
- package/services/transport/data-channel-handler.js +1373 -1373
- package/services/viewer/PriorityMap.js +421 -421
- package/test/audio-calibration.test.js +23 -23
- package/test/auto-quality-step.test.js +762 -762
- package/test/coalesce.test.js +39 -39
- package/test/coverage-follows-the-disk.test.js +5 -0
- package/test/decode-cost.test.js +475 -475
- package/test/encode-run.test.js +58 -0
- package/test/episode-naming.test.js +131 -131
- package/test/failed-encoder-still-serves-what-exists.test.js +1 -1
- package/test/fixtures/audio-frame-boundary.json +1 -1
- package/test/fixtures/refused-audio-20261002.json +10 -0
- package/test/fmp4-presentation-clock.test.js +4 -2
- package/test/format-keyed-outputs.test.js +78 -78
- package/test/health-metrics.test.js +11 -13
- package/test/held-read-ownership.test.js +77 -77
- package/test/helpers/encode-run.js +14 -6
- package/test/helpers/held-file.js +77 -77
- package/test/helpers/manager.js +62 -62
- package/test/helpers/matroska-file.js +296 -296
- package/test/keyframe-table-budget.test.js +123 -123
- package/test/matroska-cluster-discovery.test.js +323 -323
- package/test/one-output-one-session.test.js +273 -273
- package/test/output-speed.test.js +6 -1
- package/test/piece-completeness.test.js +37 -1
- package/test/quality-variants.test.js +1274 -1270
- package/test/rate-control-in-the-output.test.js +4 -3
- package/test/reads-not-yet-arrived.test.js +218 -218
- package/test/seek-frees-the-viewers-requests.test.js +1 -1
- package/test/segment-closed-on-both-branches.test.js +114 -114
- package/test/segment-judged-by-its-own-header.test.js +222 -222
- package/test/segment-serve-wiring.test.js +481 -481
- package/test/serving-existing-output.test.js +111 -111
- package/test/source-files-route.test.js +113 -113
- package/test/subtitle-cue-walk.test.js +385 -385
- package/test/subtitle-metadata.test.js +15 -15
- package/test/subtitle-plan-waits.test.js +125 -125
- package/test/subtitle-track-numbering.test.js +391 -391
- package/test/two-viewers-one-picture.test.js +508 -508
- package/test/viewer-requests-admission.test.js +92 -92
- package/test/viewer-starts-on-map.test.js +27 -27
- package/test/viewer-states.test.js +5 -2
- package/test/viewing-generations.test.js +390 -390
- package/test/whole-file-held-read.test.js +30 -30
- package/utils/coalesce.js +41 -41
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import test from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import { SubtitleFileContainer } from "../services/media/container/SubtitleFileContainer.js";
|
|
4
|
-
|
|
5
|
-
const ass = `[Script Info]\nTitle: Princess Mononoke\nYear: 1997\nMovie Title: Princess Mononoke\nScript Updated By: 2026\n\n[Events]\nFormat: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\nDialogue: 0,0:00:01.00,0:00:02.00,Default,,0,0,0,,Hello`;
|
|
6
|
-
test("ASS header evidence survives conversion without changing cues", () => {
|
|
7
|
-
const vtt = SubtitleFileContainer.toVtt(ass, ".ass");
|
|
8
|
-
const json = vtt.match(/NOTE TORRENT-TV-METADATA\n([^\n]+)/u)[1];
|
|
9
|
-
assert.deepEqual(JSON.parse(json), { titles: ["Princess Mononoke"], genericTitles: ["Princess Mononoke"], years: [1997] });
|
|
10
|
-
assert.match(vtt, /00:00:01.000 --> 00:00:02.000\nHello/u);
|
|
11
|
-
});
|
|
12
|
-
test("metadata is bounded and dialogue is not scanned", () => {
|
|
13
|
-
const vtt = SubtitleFileContainer.toVtt(ass.replace("[Script Info]", "[Other]").replace("Hello", "Movie Title: Another film"), ".ass");
|
|
14
|
-
assert.equal(vtt.includes("NOTE TORRENT-TV-METADATA"), false);
|
|
15
|
-
});
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { SubtitleFileContainer } from "../services/media/container/SubtitleFileContainer.js";
|
|
4
|
+
|
|
5
|
+
const ass = `[Script Info]\nTitle: Princess Mononoke\nYear: 1997\nMovie Title: Princess Mononoke\nScript Updated By: 2026\n\n[Events]\nFormat: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\nDialogue: 0,0:00:01.00,0:00:02.00,Default,,0,0,0,,Hello`;
|
|
6
|
+
test("ASS header evidence survives conversion without changing cues", () => {
|
|
7
|
+
const vtt = SubtitleFileContainer.toVtt(ass, ".ass");
|
|
8
|
+
const json = vtt.match(/NOTE TORRENT-TV-METADATA\n([^\n]+)/u)[1];
|
|
9
|
+
assert.deepEqual(JSON.parse(json), { titles: ["Princess Mononoke"], genericTitles: ["Princess Mononoke"], years: [1997] });
|
|
10
|
+
assert.match(vtt, /00:00:01.000 --> 00:00:02.000\nHello/u);
|
|
11
|
+
});
|
|
12
|
+
test("metadata is bounded and dialogue is not scanned", () => {
|
|
13
|
+
const vtt = SubtitleFileContainer.toVtt(ass.replace("[Script Info]", "[Other]").replace("Hello", "Movie Title: Another film"), ".ass");
|
|
14
|
+
assert.equal(vtt.includes("NOTE TORRENT-TV-METADATA"), false);
|
|
15
|
+
});
|
|
@@ -1,125 +1,125 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file The subtitle plan is kept only once it has been read; a plan whose
|
|
3
|
-
* read finishes later serves the same pass; and cues the container takes back
|
|
4
|
-
* leave by the numbers they were sent under.
|
|
5
|
-
*
|
|
6
|
-
* Fakes only: a stand-in container and a held file of plain functions.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import test from "node:test";
|
|
10
|
-
import assert from "node:assert/strict";
|
|
11
|
-
|
|
12
|
-
import { cuesHeldFor, forgetSubtitles, subtitleTracksOf, warmSubtitleCues } from "../services/media/SubtitleCues.js";
|
|
13
|
-
import { BytesUnavailable } from "../services/media/container/unavailable.js";
|
|
14
|
-
|
|
15
|
-
const PLAN = {
|
|
16
|
-
tracks: [{ trackNumber: 2, declaredIndex: 0, codecId: "S_TEXT/UTF8", language: "eng", languageSource: "default", clusterPositions: [] }],
|
|
17
|
-
declared: [],
|
|
18
|
-
secondsPerTick: 0.001,
|
|
19
|
-
segmentDataOffset: 0
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
function heldFile(sourceKey, container) {
|
|
23
|
-
return {
|
|
24
|
-
sourceKey,
|
|
25
|
-
fileIndex: 0,
|
|
26
|
-
name: "film.mkv",
|
|
27
|
-
length: 1000,
|
|
28
|
-
container: async () => container(),
|
|
29
|
-
heldRanges: async () => [[0, 999]],
|
|
30
|
-
readHeld: async () => Buffer.alloc(0)
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
test("a plan whose bytes have not arrived is not kept; the next ask reads it", async () => {
|
|
35
|
-
const sourceKey = "p".repeat(40);
|
|
36
|
-
forgetSubtitles(sourceKey);
|
|
37
|
-
let here = false;
|
|
38
|
-
const container = () => {
|
|
39
|
-
if (!here) {
|
|
40
|
-
throw new BytesUnavailable(900, 999, 0);
|
|
41
|
-
}
|
|
42
|
-
return { readSubtitlePlan: async () => PLAN, readHeldCues: async () => ({ found: new Map(), covered: 0, indexed: 0, withdrawn: [] }) };
|
|
43
|
-
};
|
|
44
|
-
try {
|
|
45
|
-
assert.deepEqual(await subtitleTracksOf(heldFile(sourceKey, container)), []);
|
|
46
|
-
here = true;
|
|
47
|
-
const tracks = await subtitleTracksOf(heldFile(sourceKey, container));
|
|
48
|
-
assert.deepEqual(tracks.map((track) => [track.trackNumber, track.language, track.languageSource]), [[2, "eng", "default"]]);
|
|
49
|
-
} finally {
|
|
50
|
-
forgetSubtitles(sourceKey);
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
test("a plan read that finishes later serves the pass that asked, with no further arrival", async () => {
|
|
55
|
-
const sourceKey = "q".repeat(40);
|
|
56
|
-
forgetSubtitles(sourceKey);
|
|
57
|
-
let release;
|
|
58
|
-
let walks = 0;
|
|
59
|
-
const arrived = new Promise((resolve) => {
|
|
60
|
-
release = resolve;
|
|
61
|
-
});
|
|
62
|
-
const container = {
|
|
63
|
-
readSubtitlePlan: async () => {
|
|
64
|
-
await arrived;
|
|
65
|
-
return PLAN;
|
|
66
|
-
},
|
|
67
|
-
// A cluster is read once: the first pass finds the line, later ones nothing.
|
|
68
|
-
readHeldCues: async () => {
|
|
69
|
-
walks += 1;
|
|
70
|
-
return {
|
|
71
|
-
found: walks === 1 ? new Map([[2, [{ startSeconds: 1, endSeconds: 2, text: "line", source: 77 }]]]) : new Map(),
|
|
72
|
-
covered: 1,
|
|
73
|
-
indexed: 1,
|
|
74
|
-
withdrawn: []
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
try {
|
|
79
|
-
// The file is one somebody asked about: a browser's seed is under way.
|
|
80
|
-
const seed = cuesHeldFor(heldFile(sourceKey, () => container), 2);
|
|
81
|
-
const warm = warmSubtitleCues(heldFile(sourceKey, () => container));
|
|
82
|
-
release();
|
|
83
|
-
await seed;
|
|
84
|
-
const entries = await warm;
|
|
85
|
-
assert.equal(entries.length, 1, "the one pass pushed what the plan led to");
|
|
86
|
-
assert.deepEqual(entries[0].cues.map((cue) => [cue.text, cue.seq]), [["line", 1]]);
|
|
87
|
-
} finally {
|
|
88
|
-
forgetSubtitles(sourceKey);
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
test("cues read from a position the container withdraws are taken back by their numbers", async () => {
|
|
93
|
-
const sourceKey = "r".repeat(40);
|
|
94
|
-
forgetSubtitles(sourceKey);
|
|
95
|
-
let passNumber = 0;
|
|
96
|
-
const container = {
|
|
97
|
-
readSubtitlePlan: async () => PLAN,
|
|
98
|
-
readHeldCues: async () => {
|
|
99
|
-
passNumber += 1;
|
|
100
|
-
return passNumber === 1
|
|
101
|
-
? {
|
|
102
|
-
found: new Map([[2, [
|
|
103
|
-
{ startSeconds: 1, endSeconds: 2, text: "real", source: 10 },
|
|
104
|
-
{ startSeconds: 3, endSeconds: 4, text: "false", source: 50 }
|
|
105
|
-
]]]),
|
|
106
|
-
covered: 2,
|
|
107
|
-
indexed: 2,
|
|
108
|
-
withdrawn: []
|
|
109
|
-
}
|
|
110
|
-
: { found: new Map(), covered: 1, indexed: 1, withdrawn: [50] };
|
|
111
|
-
}
|
|
112
|
-
};
|
|
113
|
-
try {
|
|
114
|
-
await subtitleTracksOf(heldFile(sourceKey, () => container));
|
|
115
|
-
const first = await warmSubtitleCues(heldFile(sourceKey, () => container));
|
|
116
|
-
assert.deepEqual(first[0].cues.map((cue) => [cue.text, cue.seq]), [["real", 1], ["false", 2]]);
|
|
117
|
-
const second = await warmSubtitleCues(heldFile(sourceKey, () => container));
|
|
118
|
-
assert.deepEqual(second[0].withdrawn, [2], "the false cue is named by the number it was sent under");
|
|
119
|
-
assert.deepEqual(second[0].cues, []);
|
|
120
|
-
const held = await cuesHeldFor(heldFile(sourceKey, () => container), 2);
|
|
121
|
-
assert.deepEqual(held.cues.map((cue) => cue.text), ["real"], "and it is gone from what is held");
|
|
122
|
-
} finally {
|
|
123
|
-
forgetSubtitles(sourceKey);
|
|
124
|
-
}
|
|
125
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* @file The subtitle plan is kept only once it has been read; a plan whose
|
|
3
|
+
* read finishes later serves the same pass; and cues the container takes back
|
|
4
|
+
* leave by the numbers they were sent under.
|
|
5
|
+
*
|
|
6
|
+
* Fakes only: a stand-in container and a held file of plain functions.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import test from "node:test";
|
|
10
|
+
import assert from "node:assert/strict";
|
|
11
|
+
|
|
12
|
+
import { cuesHeldFor, forgetSubtitles, subtitleTracksOf, warmSubtitleCues } from "../services/media/SubtitleCues.js";
|
|
13
|
+
import { BytesUnavailable } from "../services/media/container/unavailable.js";
|
|
14
|
+
|
|
15
|
+
const PLAN = {
|
|
16
|
+
tracks: [{ trackNumber: 2, declaredIndex: 0, codecId: "S_TEXT/UTF8", language: "eng", languageSource: "default", clusterPositions: [] }],
|
|
17
|
+
declared: [],
|
|
18
|
+
secondsPerTick: 0.001,
|
|
19
|
+
segmentDataOffset: 0
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
function heldFile(sourceKey, container) {
|
|
23
|
+
return {
|
|
24
|
+
sourceKey,
|
|
25
|
+
fileIndex: 0,
|
|
26
|
+
name: "film.mkv",
|
|
27
|
+
length: 1000,
|
|
28
|
+
container: async () => container(),
|
|
29
|
+
heldRanges: async () => [[0, 999]],
|
|
30
|
+
readHeld: async () => Buffer.alloc(0)
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
test("a plan whose bytes have not arrived is not kept; the next ask reads it", async () => {
|
|
35
|
+
const sourceKey = "p".repeat(40);
|
|
36
|
+
forgetSubtitles(sourceKey);
|
|
37
|
+
let here = false;
|
|
38
|
+
const container = () => {
|
|
39
|
+
if (!here) {
|
|
40
|
+
throw new BytesUnavailable(900, 999, 0);
|
|
41
|
+
}
|
|
42
|
+
return { readSubtitlePlan: async () => PLAN, readHeldCues: async () => ({ found: new Map(), covered: 0, indexed: 0, withdrawn: [] }) };
|
|
43
|
+
};
|
|
44
|
+
try {
|
|
45
|
+
assert.deepEqual(await subtitleTracksOf(heldFile(sourceKey, container)), []);
|
|
46
|
+
here = true;
|
|
47
|
+
const tracks = await subtitleTracksOf(heldFile(sourceKey, container));
|
|
48
|
+
assert.deepEqual(tracks.map((track) => [track.trackNumber, track.language, track.languageSource]), [[2, "eng", "default"]]);
|
|
49
|
+
} finally {
|
|
50
|
+
forgetSubtitles(sourceKey);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("a plan read that finishes later serves the pass that asked, with no further arrival", async () => {
|
|
55
|
+
const sourceKey = "q".repeat(40);
|
|
56
|
+
forgetSubtitles(sourceKey);
|
|
57
|
+
let release;
|
|
58
|
+
let walks = 0;
|
|
59
|
+
const arrived = new Promise((resolve) => {
|
|
60
|
+
release = resolve;
|
|
61
|
+
});
|
|
62
|
+
const container = {
|
|
63
|
+
readSubtitlePlan: async () => {
|
|
64
|
+
await arrived;
|
|
65
|
+
return PLAN;
|
|
66
|
+
},
|
|
67
|
+
// A cluster is read once: the first pass finds the line, later ones nothing.
|
|
68
|
+
readHeldCues: async () => {
|
|
69
|
+
walks += 1;
|
|
70
|
+
return {
|
|
71
|
+
found: walks === 1 ? new Map([[2, [{ startSeconds: 1, endSeconds: 2, text: "line", source: 77 }]]]) : new Map(),
|
|
72
|
+
covered: 1,
|
|
73
|
+
indexed: 1,
|
|
74
|
+
withdrawn: []
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
try {
|
|
79
|
+
// The file is one somebody asked about: a browser's seed is under way.
|
|
80
|
+
const seed = cuesHeldFor(heldFile(sourceKey, () => container), 2);
|
|
81
|
+
const warm = warmSubtitleCues(heldFile(sourceKey, () => container));
|
|
82
|
+
release();
|
|
83
|
+
await seed;
|
|
84
|
+
const entries = await warm;
|
|
85
|
+
assert.equal(entries.length, 1, "the one pass pushed what the plan led to");
|
|
86
|
+
assert.deepEqual(entries[0].cues.map((cue) => [cue.text, cue.seq]), [["line", 1]]);
|
|
87
|
+
} finally {
|
|
88
|
+
forgetSubtitles(sourceKey);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test("cues read from a position the container withdraws are taken back by their numbers", async () => {
|
|
93
|
+
const sourceKey = "r".repeat(40);
|
|
94
|
+
forgetSubtitles(sourceKey);
|
|
95
|
+
let passNumber = 0;
|
|
96
|
+
const container = {
|
|
97
|
+
readSubtitlePlan: async () => PLAN,
|
|
98
|
+
readHeldCues: async () => {
|
|
99
|
+
passNumber += 1;
|
|
100
|
+
return passNumber === 1
|
|
101
|
+
? {
|
|
102
|
+
found: new Map([[2, [
|
|
103
|
+
{ startSeconds: 1, endSeconds: 2, text: "real", source: 10 },
|
|
104
|
+
{ startSeconds: 3, endSeconds: 4, text: "false", source: 50 }
|
|
105
|
+
]]]),
|
|
106
|
+
covered: 2,
|
|
107
|
+
indexed: 2,
|
|
108
|
+
withdrawn: []
|
|
109
|
+
}
|
|
110
|
+
: { found: new Map(), covered: 1, indexed: 1, withdrawn: [50] };
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
try {
|
|
114
|
+
await subtitleTracksOf(heldFile(sourceKey, () => container));
|
|
115
|
+
const first = await warmSubtitleCues(heldFile(sourceKey, () => container));
|
|
116
|
+
assert.deepEqual(first[0].cues.map((cue) => [cue.text, cue.seq]), [["real", 1], ["false", 2]]);
|
|
117
|
+
const second = await warmSubtitleCues(heldFile(sourceKey, () => container));
|
|
118
|
+
assert.deepEqual(second[0].withdrawn, [2], "the false cue is named by the number it was sent under");
|
|
119
|
+
assert.deepEqual(second[0].cues, []);
|
|
120
|
+
const held = await cuesHeldFor(heldFile(sourceKey, () => container), 2);
|
|
121
|
+
assert.deepEqual(held.cues.map((cue) => cue.text), ["real"], "and it is gone from what is held");
|
|
122
|
+
} finally {
|
|
123
|
+
forgetSubtitles(sourceKey);
|
|
124
|
+
}
|
|
125
|
+
});
|