@torrent-tv/proxy 2.80.18 → 2.81.0

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 (67) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/docs/encode-architecture.md +51 -2
  3. package/package.json +1 -1
  4. package/research/double-spawn-2026-09-10.md +171 -0
  5. package/services/disk/DiskSpace.js +146 -0
  6. package/services/disk/wire.js +60 -0
  7. package/services/encode/EncodeRun.js +37 -9
  8. package/services/encode/SegmentStore.js +284 -232
  9. package/services/encode/run-command.js +16 -1
  10. package/services/hls-session-manager.js +31 -128
  11. package/services/orchestrators/EncodeOrchestrator.js +52 -38
  12. package/services/piece-store/allowance.js +107 -0
  13. package/services/piece-store/piece-disk-store.js +365 -0
  14. package/services/piece-store/shared-piece-store.js +1549 -1535
  15. package/services/segment-formats/fmp4.js +54 -0
  16. package/services/segment-formats/mpegts.js +54 -0
  17. package/services/torrent-worker/client.js +32 -0
  18. package/services/torrent-worker/pool-adapter.js +15 -0
  19. package/services/torrent-worker/protocol.js +9 -0
  20. package/services/torrent-worker/worker.js +8 -1
  21. package/services/viewer/positions.js +48 -0
  22. package/test/audio-inventory.test.js +176 -176
  23. package/test/auto-quality-step.test.js +514 -514
  24. package/test/concurrent-cost.test.js +138 -138
  25. package/test/coverage-follows-the-disk.test.js +191 -187
  26. package/test/coverage-map.test.js +195 -195
  27. package/test/declared-tracks.test.js +35 -35
  28. package/test/disk-space.test.js +138 -0
  29. package/test/encode-orchestrator.test.js +0 -3
  30. package/test/encode-run.test.js +5 -12
  31. package/test/held-request-width.test.js +155 -155
  32. package/test/helpers/encode-run.js +2 -2
  33. package/test/matroska-blocks.test.js +0 -0
  34. package/test/matroska-cues-track.test.js +192 -192
  35. package/test/mp4-composition-times.test.js +0 -0
  36. package/test/mp4-subtitles.test.js +173 -173
  37. package/test/one-authority.test.js +281 -220
  38. package/test/orchestrator-wired.test.js +199 -195
  39. package/test/packet-witness-ring.test.js +236 -236
  40. package/test/packet-witness.test.js +148 -148
  41. package/test/piece-disk-store.test.js +267 -0
  42. package/test/piece-reader.test.js +4 -4
  43. package/test/piece-store-eviction.test.js +17 -17
  44. package/test/piece-store-reservations.test.js +20 -1
  45. package/test/piece-store-slow-disk.test.js +16 -1
  46. package/test/produced-copy-choice.test.js +258 -358
  47. package/test/read-window.test.js +6 -6
  48. package/test/run-intervals.test.js +100 -100
  49. package/test/seek-landing.test.js +109 -109
  50. package/test/segment-serve-wiring.test.js +8 -9
  51. package/test/segment-store-eviction.test.js +232 -0
  52. package/test/segment-store.test.js +238 -216
  53. package/test/segments-are-shared.test.js +1 -1
  54. package/test/shared-piece-store.test.js +12 -12
  55. package/test/sidecar-naming.test.js +142 -142
  56. package/test/subtitle-cue-framing.test.js +200 -200
  57. package/test/subtitle-cue-walk.test.js +369 -369
  58. package/test/subtitle-defaults.test.js +97 -97
  59. package/test/subtitle-track-numbering.test.js +370 -370
  60. package/test/tail-duplication.test.js +167 -167
  61. package/test/tracks-begin-together.test.js +195 -195
  62. package/test/two-viewers-one-picture.test.js +374 -374
  63. package/test/video-facts.test.js +102 -102
  64. package/test/wedge-certainty.test.js +131 -131
  65. package/services/encode/open-piece.js +0 -135
  66. package/services/piece-store/disk-tier.js +0 -151
  67. package/test/open-piece.test.js +0 -152
@@ -1,138 +1,138 @@
1
- /**
2
- * @file A rung is judged against the machine it will actually have.
3
- *
4
- * The field case of 2026-08-15, in arithmetic: a copy of the picture took about
5
- * 0.125 s of work per second of video (7.9-8.0x measured), a 240p rung needed
6
- * about 1.05, and the two together are more than the one second per second the
7
- * machine has. The budget priced the copy at nothing, offered the rung, and the
8
- * viewer watched it fail.
9
- */
10
-
11
- import assert from "node:assert/strict";
12
- import test from "node:test";
13
-
14
- import { canSustainOutput } from "../services/hwaccel.js";
15
-
16
- // A host that encodes 640x360 at 24 fps about eleven times over, and decodes
17
- // 1080p24 at 2.6x — the addon host's own figures.
18
- const BENCHMARK = [
19
- { preset: "fast", pixelsPerSec: 17.0e6 },
20
- { preset: "ultrafast", pixelsPerSec: 67.5e6 }
21
- ];
22
- const DECODE_MODEL = { pixelTerm: 0.007742, bitrateTerm: 0, constantTerm: 0 };
23
- const SOURCE = { megapixelsPerSecond: (1920 * 1080 * 24) / 1e6, megabitsPerSecond: 8 };
24
- const RUNG_240P = 426 * 240 * 24;
25
-
26
- test("a rung is judged on the machine it will have, not on an empty one", () => {
27
- const alone = canSustainOutput({
28
- benchmark: BENCHMARK,
29
- decodeModel: DECODE_MODEL,
30
- source: SOURCE,
31
- outputPixelsPerSec: RUNG_240P
32
- });
33
- const beside = canSustainOutput({
34
- benchmark: BENCHMARK,
35
- decodeModel: DECODE_MODEL,
36
- source: SOURCE,
37
- outputPixelsPerSec: RUNG_240P,
38
- // The picture is being copied beside it, at the cost the field measured.
39
- concurrentCostSec: 0.125
40
- });
41
- assert.ok(alone.speed !== null && beside.speed !== null);
42
- assert.ok(beside.speed < alone.speed, "sharing the machine cannot make a rung faster");
43
- });
44
-
45
- test("nothing else running leaves the answer exactly as it was", () => {
46
- const withZero = canSustainOutput({
47
- benchmark: BENCHMARK,
48
- decodeModel: DECODE_MODEL,
49
- source: SOURCE,
50
- outputPixelsPerSec: RUNG_240P,
51
- concurrentCostSec: 0
52
- });
53
- const without = canSustainOutput({
54
- benchmark: BENCHMARK,
55
- decodeModel: DECODE_MODEL,
56
- source: SOURCE,
57
- outputPixelsPerSec: RUNG_240P
58
- });
59
- assert.equal(withZero.speed, without.speed);
60
- });
61
-
62
- test("a host with nothing measured still refuses nothing", () => {
63
- const answer = canSustainOutput({
64
- benchmark: [],
65
- outputPixelsPerSec: RUNG_240P,
66
- concurrentCostSec: 0.125
67
- });
68
- assert.equal(answer.sustainable, true);
69
- assert.equal(answer.speed, null);
70
- });
71
-
72
- test("a cost heavy enough to fill the machine puts a rung below realtime", () => {
73
- const answer = canSustainOutput({
74
- benchmark: BENCHMARK,
75
- decodeModel: DECODE_MODEL,
76
- source: SOURCE,
77
- outputPixelsPerSec: RUNG_240P,
78
- concurrentCostSec: 0.9
79
- });
80
- assert.ok(answer.speed !== null && answer.speed < 1, "0.9 of the machine spent elsewhere leaves less than realtime");
81
- assert.equal(answer.sustainable, false);
82
- });
83
-
84
- test("a soundtrack encoder is not free once it has been measured", () => {
85
- // The second half of roadmap item 6. An audio rendition runs for as long as
86
- // the picture does — it is a second encoder by construction, not an extra —
87
- // and the budget counted it at nothing. Small beside a picture, and small is
88
- // not zero: on a host where a rung needs almost the whole machine, this is
89
- // the difference between offering it and refusing it.
90
- const withoutAudio = canSustainOutput({
91
- benchmark: BENCHMARK,
92
- decodeModel: DECODE_MODEL,
93
- source: SOURCE,
94
- outputPixelsPerSec: RUNG_240P,
95
- concurrentCostSec: 0.125
96
- });
97
- const withAudio = canSustainOutput({
98
- benchmark: BENCHMARK,
99
- decodeModel: DECODE_MODEL,
100
- source: SOURCE,
101
- outputPixelsPerSec: RUNG_240P,
102
- // The copy, plus a soundtrack measured at about twenty times realtime.
103
- concurrentCostSec: 0.125 + 0.05
104
- });
105
- assert.ok(
106
- withAudio.speed < withoutAudio.speed,
107
- "charging for the soundtrack must lower what the machine has left for the picture"
108
- );
109
- });
110
-
111
- test("two pictures encoding at once are charged as two", () => {
112
- // The warm-up that makes a quality switch seamless runs two encoders on
113
- // purpose. Measured 2026-08-15: the new rung ran at 0.504x for its first four
114
- // seconds, against 0.90-0.99x once it had the machine to itself and 1.58x
115
- // predicted for an idle one. Priced as though it were alone, the budget
116
- // describes a machine that does not exist at the moment the viewer switches.
117
- const alone = canSustainOutput({
118
- benchmark: BENCHMARK,
119
- decodeModel: DECODE_MODEL,
120
- source: SOURCE,
121
- outputPixelsPerSec: RUNG_240P,
122
- concurrentCostSec: 0
123
- });
124
- const besideAnother = canSustainOutput({
125
- benchmark: BENCHMARK,
126
- decodeModel: DECODE_MODEL,
127
- source: SOURCE,
128
- outputPixelsPerSec: RUNG_240P,
129
- // Another rung of the same family, already running at about realtime.
130
- concurrentCostSec: 1 / 1.05
131
- });
132
- assert.ok(alone.sustainable, "the rung must be offerable on an idle machine, or the case is not the one meant");
133
- assert.equal(
134
- besideAnother.sustainable,
135
- false,
136
- "a machine already spending a second per second of video has nothing left for a second picture"
137
- );
138
- });
1
+ /**
2
+ * @file A rung is judged against the machine it will actually have.
3
+ *
4
+ * The field case of 2026-08-15, in arithmetic: a copy of the picture took about
5
+ * 0.125 s of work per second of video (7.9-8.0x measured), a 240p rung needed
6
+ * about 1.05, and the two together are more than the one second per second the
7
+ * machine has. The budget priced the copy at nothing, offered the rung, and the
8
+ * viewer watched it fail.
9
+ */
10
+
11
+ import assert from "node:assert/strict";
12
+ import test from "node:test";
13
+
14
+ import { canSustainOutput } from "../services/hwaccel.js";
15
+
16
+ // A host that encodes 640x360 at 24 fps about eleven times over, and decodes
17
+ // 1080p24 at 2.6x — the addon host's own figures.
18
+ const BENCHMARK = [
19
+ { preset: "fast", pixelsPerSec: 17.0e6 },
20
+ { preset: "ultrafast", pixelsPerSec: 67.5e6 }
21
+ ];
22
+ const DECODE_MODEL = { pixelTerm: 0.007742, bitrateTerm: 0, constantTerm: 0 };
23
+ const SOURCE = { megapixelsPerSecond: (1920 * 1080 * 24) / 1e6, megabitsPerSecond: 8 };
24
+ const RUNG_240P = 426 * 240 * 24;
25
+
26
+ test("a rung is judged on the machine it will have, not on an empty one", () => {
27
+ const alone = canSustainOutput({
28
+ benchmark: BENCHMARK,
29
+ decodeModel: DECODE_MODEL,
30
+ source: SOURCE,
31
+ outputPixelsPerSec: RUNG_240P
32
+ });
33
+ const beside = canSustainOutput({
34
+ benchmark: BENCHMARK,
35
+ decodeModel: DECODE_MODEL,
36
+ source: SOURCE,
37
+ outputPixelsPerSec: RUNG_240P,
38
+ // The picture is being copied beside it, at the cost the field measured.
39
+ concurrentCostSec: 0.125
40
+ });
41
+ assert.ok(alone.speed !== null && beside.speed !== null);
42
+ assert.ok(beside.speed < alone.speed, "sharing the machine cannot make a rung faster");
43
+ });
44
+
45
+ test("nothing else running leaves the answer exactly as it was", () => {
46
+ const withZero = canSustainOutput({
47
+ benchmark: BENCHMARK,
48
+ decodeModel: DECODE_MODEL,
49
+ source: SOURCE,
50
+ outputPixelsPerSec: RUNG_240P,
51
+ concurrentCostSec: 0
52
+ });
53
+ const without = canSustainOutput({
54
+ benchmark: BENCHMARK,
55
+ decodeModel: DECODE_MODEL,
56
+ source: SOURCE,
57
+ outputPixelsPerSec: RUNG_240P
58
+ });
59
+ assert.equal(withZero.speed, without.speed);
60
+ });
61
+
62
+ test("a host with nothing measured still refuses nothing", () => {
63
+ const answer = canSustainOutput({
64
+ benchmark: [],
65
+ outputPixelsPerSec: RUNG_240P,
66
+ concurrentCostSec: 0.125
67
+ });
68
+ assert.equal(answer.sustainable, true);
69
+ assert.equal(answer.speed, null);
70
+ });
71
+
72
+ test("a cost heavy enough to fill the machine puts a rung below realtime", () => {
73
+ const answer = canSustainOutput({
74
+ benchmark: BENCHMARK,
75
+ decodeModel: DECODE_MODEL,
76
+ source: SOURCE,
77
+ outputPixelsPerSec: RUNG_240P,
78
+ concurrentCostSec: 0.9
79
+ });
80
+ assert.ok(answer.speed !== null && answer.speed < 1, "0.9 of the machine spent elsewhere leaves less than realtime");
81
+ assert.equal(answer.sustainable, false);
82
+ });
83
+
84
+ test("a soundtrack encoder is not free once it has been measured", () => {
85
+ // The second half of roadmap item 6. An audio rendition runs for as long as
86
+ // the picture does — it is a second encoder by construction, not an extra —
87
+ // and the budget counted it at nothing. Small beside a picture, and small is
88
+ // not zero: on a host where a rung needs almost the whole machine, this is
89
+ // the difference between offering it and refusing it.
90
+ const withoutAudio = canSustainOutput({
91
+ benchmark: BENCHMARK,
92
+ decodeModel: DECODE_MODEL,
93
+ source: SOURCE,
94
+ outputPixelsPerSec: RUNG_240P,
95
+ concurrentCostSec: 0.125
96
+ });
97
+ const withAudio = canSustainOutput({
98
+ benchmark: BENCHMARK,
99
+ decodeModel: DECODE_MODEL,
100
+ source: SOURCE,
101
+ outputPixelsPerSec: RUNG_240P,
102
+ // The copy, plus a soundtrack measured at about twenty times realtime.
103
+ concurrentCostSec: 0.125 + 0.05
104
+ });
105
+ assert.ok(
106
+ withAudio.speed < withoutAudio.speed,
107
+ "charging for the soundtrack must lower what the machine has left for the picture"
108
+ );
109
+ });
110
+
111
+ test("two pictures encoding at once are charged as two", () => {
112
+ // The warm-up that makes a quality switch seamless runs two encoders on
113
+ // purpose. Measured 2026-08-15: the new rung ran at 0.504x for its first four
114
+ // seconds, against 0.90-0.99x once it had the machine to itself and 1.58x
115
+ // predicted for an idle one. Priced as though it were alone, the budget
116
+ // describes a machine that does not exist at the moment the viewer switches.
117
+ const alone = canSustainOutput({
118
+ benchmark: BENCHMARK,
119
+ decodeModel: DECODE_MODEL,
120
+ source: SOURCE,
121
+ outputPixelsPerSec: RUNG_240P,
122
+ concurrentCostSec: 0
123
+ });
124
+ const besideAnother = canSustainOutput({
125
+ benchmark: BENCHMARK,
126
+ decodeModel: DECODE_MODEL,
127
+ source: SOURCE,
128
+ outputPixelsPerSec: RUNG_240P,
129
+ // Another rung of the same family, already running at about realtime.
130
+ concurrentCostSec: 1 / 1.05
131
+ });
132
+ assert.ok(alone.sustainable, "the rung must be offerable on an idle machine, or the case is not the one meant");
133
+ assert.equal(
134
+ besideAnother.sustainable,
135
+ false,
136
+ "a machine already spending a second per second of video has nothing left for a second picture"
137
+ );
138
+ });