@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.
Files changed (92) hide show
  1. package/AGENTS.md +32 -12
  2. package/CHANGELOG.md +2340 -2330
  3. package/assets/calibration/audio/NOTICE.md +19 -19
  4. package/assets/calibration/audio/manifest.json +98 -98
  5. package/biome.json +412 -412
  6. package/docs/browser-proxy-contract.md +155 -155
  7. package/docs/container-architecture.md +273 -273
  8. package/docs/encode-run-state.md +1 -1
  9. package/docs/playback-start-forecast.md +113 -98
  10. package/package.json +51 -50
  11. package/routes/api/sources/files/get.js +106 -106
  12. package/scripts/build-audio-samples.mjs +31 -31
  13. package/services/encode/EncodeRun.js +67 -15
  14. package/services/encode/EncodeRuns.js +19 -7
  15. package/services/encode/OutputOpening.js +784 -784
  16. package/services/encode/audio-calibration.js +67 -67
  17. package/services/encode/audio-work-rate.js +15 -15
  18. package/services/encode/encode-exit.js +2 -0
  19. package/services/encode/hwaccel.js +1894 -1894
  20. package/services/encode/output/index.js +24 -24
  21. package/services/encode/piece-completeness.js +60 -2
  22. package/services/encode/run-command.js +757 -755
  23. package/services/encode/segment-formats/index.js +1 -0
  24. package/services/encode/segment-formats/mp4-boxes.js +8 -1
  25. package/services/media/ContainerOrchestrator.js +202 -202
  26. package/services/media/KeyframeTables.js +356 -356
  27. package/services/media/SubtitleCues.js +592 -592
  28. package/services/media/audio-inventory.js +299 -299
  29. package/services/media/container/AviContainer.js +268 -268
  30. package/services/media/container/Container.js +632 -632
  31. package/services/media/container/ContainerFactory.js +38 -38
  32. package/services/media/container/MatroskaContainer.js +1031 -1031
  33. package/services/media/container/Mp4Container.js +1354 -1354
  34. package/services/media/container/SubtitleFileContainer.js +339 -339
  35. package/services/media/container/ebml-stream.js +174 -174
  36. package/services/media/container/matroska-clusters.js +1165 -1165
  37. package/services/media/container/unavailable.js +77 -77
  38. package/services/media/tracks/ContainerTrack.js +84 -84
  39. package/services/media/tracks/TextSubtitleTrack.js +295 -295
  40. package/services/storage/piece-store/shared-piece-store.js +1937 -1937
  41. package/services/storage/segment-store/SegmentStore.js +1294 -1294
  42. package/services/torrent/Contents.js +382 -382
  43. package/services/torrent/episode-naming.js +182 -182
  44. package/services/torrent/torrent-pool.js +2688 -2688
  45. package/services/torrent/worker/client.js +836 -836
  46. package/services/torrent/worker/held-bytes.js +231 -231
  47. package/services/torrent/worker/worker.js +1137 -1137
  48. package/services/transport/data-channel-handler.js +1373 -1373
  49. package/services/viewer/PriorityMap.js +421 -421
  50. package/test/audio-calibration.test.js +23 -23
  51. package/test/auto-quality-step.test.js +762 -762
  52. package/test/coalesce.test.js +39 -39
  53. package/test/coverage-follows-the-disk.test.js +5 -0
  54. package/test/decode-cost.test.js +475 -475
  55. package/test/encode-run.test.js +58 -0
  56. package/test/episode-naming.test.js +131 -131
  57. package/test/failed-encoder-still-serves-what-exists.test.js +1 -1
  58. package/test/fixtures/audio-frame-boundary.json +1 -1
  59. package/test/fixtures/refused-audio-20261002.json +10 -0
  60. package/test/fmp4-presentation-clock.test.js +4 -2
  61. package/test/format-keyed-outputs.test.js +78 -78
  62. package/test/health-metrics.test.js +11 -13
  63. package/test/held-read-ownership.test.js +77 -77
  64. package/test/helpers/encode-run.js +14 -6
  65. package/test/helpers/held-file.js +77 -77
  66. package/test/helpers/manager.js +62 -62
  67. package/test/helpers/matroska-file.js +296 -296
  68. package/test/keyframe-table-budget.test.js +123 -123
  69. package/test/matroska-cluster-discovery.test.js +323 -323
  70. package/test/one-output-one-session.test.js +273 -273
  71. package/test/output-speed.test.js +6 -1
  72. package/test/piece-completeness.test.js +37 -1
  73. package/test/quality-variants.test.js +1274 -1270
  74. package/test/rate-control-in-the-output.test.js +4 -3
  75. package/test/reads-not-yet-arrived.test.js +218 -218
  76. package/test/seek-frees-the-viewers-requests.test.js +1 -1
  77. package/test/segment-closed-on-both-branches.test.js +114 -114
  78. package/test/segment-judged-by-its-own-header.test.js +222 -222
  79. package/test/segment-serve-wiring.test.js +481 -481
  80. package/test/serving-existing-output.test.js +111 -111
  81. package/test/source-files-route.test.js +113 -113
  82. package/test/subtitle-cue-walk.test.js +385 -385
  83. package/test/subtitle-metadata.test.js +15 -15
  84. package/test/subtitle-plan-waits.test.js +125 -125
  85. package/test/subtitle-track-numbering.test.js +391 -391
  86. package/test/two-viewers-one-picture.test.js +508 -508
  87. package/test/viewer-requests-admission.test.js +92 -92
  88. package/test/viewer-starts-on-map.test.js +27 -27
  89. package/test/viewer-states.test.js +5 -2
  90. package/test/viewing-generations.test.js +390 -390
  91. package/test/whole-file-held-read.test.js +30 -30
  92. 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
+ });