@torrent-tv/proxy 2.89.17 → 2.89.19

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 (300) hide show
  1. package/README.md +14 -3
  2. package/package.json +10 -2
  3. package/server.js +0 -11
  4. package/utils/client-log-file.js +147 -42
  5. package/.claude/commands/opsx/apply.md +0 -155
  6. package/.claude/commands/opsx/archive.md +0 -160
  7. package/.claude/commands/opsx/explore.md +0 -174
  8. package/.claude/commands/opsx/propose.md +0 -109
  9. package/.claude/commands/opsx/sync.md +0 -143
  10. package/.claude/skills/openspec-apply-change/SKILL.md +0 -159
  11. package/.claude/skills/openspec-archive-change/SKILL.md +0 -117
  12. package/.claude/skills/openspec-explore/SKILL.md +0 -289
  13. package/.claude/skills/openspec-propose/SKILL.md +0 -113
  14. package/.claude/skills/openspec-sync-specs/SKILL.md +0 -147
  15. package/.dockerignore +0 -5
  16. package/AGENTS.md +0 -363
  17. package/CHANGELOG.md +0 -2377
  18. package/Dockerfile +0 -30
  19. package/biome.json +0 -412
  20. package/corpus.tmp.json +0 -1
  21. package/docs/browser-proxy-contract.md +0 -155
  22. package/docs/container-architecture.md +0 -273
  23. package/docs/download-architecture.md +0 -261
  24. package/docs/encode-architecture.md +0 -765
  25. package/docs/encode-run-state.md +0 -96
  26. package/docs/logs.md +0 -151
  27. package/docs/playback-start-forecast.md +0 -113
  28. package/docs/socks5-egress-research.md +0 -114
  29. package/docs/storage-architecture.md +0 -273
  30. package/knip.json +0 -14
  31. package/main2.cpuprofile +0 -1
  32. package/openspec/changes/adaptive-bitrate/.openspec.yaml +0 -0
  33. package/openspec/changes/adaptive-bitrate/design.md +0 -92
  34. package/openspec/changes/adaptive-bitrate/proposal.md +0 -56
  35. package/openspec/changes/adaptive-bitrate/specs/adaptive-bitrate/spec.md +0 -51
  36. package/openspec/changes/adaptive-bitrate/tasks.md +0 -40
  37. package/openspec/changes/binary-distribution/.openspec.yaml +0 -2
  38. package/openspec/changes/binary-distribution/proposal.md +0 -53
  39. package/openspec/changes/chunked-request-bodies/design.md +0 -81
  40. package/openspec/changes/chunked-request-bodies/proposal.md +0 -53
  41. package/openspec/changes/chunked-request-bodies/specs/chunked-request-bodies/spec.md +0 -30
  42. package/openspec/changes/chunked-request-bodies/tasks.md +0 -40
  43. package/openspec/changes/cold-start/design.md +0 -98
  44. package/openspec/changes/cold-start/proposal.md +0 -62
  45. package/openspec/changes/cold-start/specs/cold-start/spec.md +0 -47
  46. package/openspec/changes/cold-start/tasks.md +0 -48
  47. package/openspec/changes/disk-cap/.openspec.yaml +0 -2
  48. package/openspec/changes/disk-cap/proposal.md +0 -37
  49. package/openspec/changes/disk-cap/specs/disk-cap/spec.md +0 -25
  50. package/openspec/changes/disk-cap/tasks.md +0 -19
  51. package/openspec/changes/proxy-observability/.openspec.yaml +0 -2
  52. package/openspec/changes/proxy-observability/design.md +0 -38
  53. package/openspec/changes/proxy-observability/proposal.md +0 -49
  54. package/openspec/changes/proxy-observability/specs/observability/spec.md +0 -33
  55. package/openspec/changes/proxy-observability/tasks.md +0 -21
  56. package/openspec/changes/seek-debounce/.openspec.yaml +0 -0
  57. package/openspec/changes/seek-debounce/design.md +0 -118
  58. package/openspec/changes/seek-debounce/proposal.md +0 -59
  59. package/openspec/changes/seek-debounce/specs/seek-debounce/spec.md +0 -41
  60. package/openspec/changes/seek-debounce/tasks.md +0 -33
  61. package/openspec/changes/subtitle-language/.openspec.yaml +0 -2
  62. package/openspec/changes/subtitle-language/proposal.md +0 -50
  63. package/openspec/changes/subtitle-language/specs/subtitle-language/spec.md +0 -34
  64. package/openspec/changes/subtitle-language/tasks.md +0 -26
  65. package/openspec/changes/track-selection/.openspec.yaml +0 -2
  66. package/openspec/changes/track-selection/design.md +0 -47
  67. package/openspec/changes/track-selection/proposal.md +0 -45
  68. package/openspec/changes/track-selection/specs/track-selection/spec.md +0 -41
  69. package/openspec/changes/track-selection/tasks.md +0 -20
  70. package/openspec/changes/transcode-quality/.openspec.yaml +0 -2
  71. package/openspec/changes/transcode-quality/proposal.md +0 -51
  72. package/openspec/changes/transcode-quality/specs/transcode-quality/spec.md +0 -139
  73. package/openspec/changes/transcode-quality/tasks.md +0 -63
  74. package/openspec/config.yaml +0 -38
  75. package/public/.well-known/appspecific/com.chrome.devtools.json +0 -1
  76. package/research/delivery-wedge-stand-ladder-2026-09-13.md +0 -329
  77. package/research/double-spawn-2026-09-10.md +0 -171
  78. package/research/handover-2026-09-13.md +0 -209
  79. package/research/handover-reader-claims-removal-2026-09-12.md +0 -166
  80. package/research/piece-withdrawn-but-still-claimed-2026-09-12.md +0 -175
  81. package/research/preset-benchmark-size-scaling-2026-08-22.md +0 -55
  82. package/research/priority-map-is-the-truth-2026-09-12.md +0 -225
  83. package/research/valgrind-replica-2026-08-27/logs.tar.gz +0 -0
  84. package/scripts/build-audio-samples.mjs +0 -31
  85. package/scripts/render-run-graph.js +0 -173
  86. package/test/asset-paths.test.js +0 -28
  87. package/test/audio-buffer-continuity.test.js +0 -77
  88. package/test/audio-calibration.test.js +0 -23
  89. package/test/audio-inventory.test.js +0 -212
  90. package/test/audio-track-bitrate.test.js +0 -40
  91. package/test/audio-warm-recorded.test.js +0 -63
  92. package/test/auto-quality-step.test.js +0 -762
  93. package/test/available-share.test.js +0 -76
  94. package/test/background-fill.test.js +0 -190
  95. package/test/client-log-file.test.js +0 -126
  96. package/test/client-logs-route.test.js +0 -34
  97. package/test/coalesce.test.js +0 -39
  98. package/test/completed-files.test.js +0 -115
  99. package/test/concurrent-cost.test.js +0 -148
  100. package/test/container-media-info.test.js +0 -228
  101. package/test/contention.test.js +0 -76
  102. package/test/core-dumps.test.js +0 -57
  103. package/test/coverage-follows-the-disk.test.js +0 -233
  104. package/test/coverage-map.test.js +0 -195
  105. package/test/cue-push-carries-what-the-walk-found.test.js +0 -58
  106. package/test/cut-times-timeline.test.js +0 -65
  107. package/test/cuts-follow-published-grid.test.js +0 -109
  108. package/test/data-channel-frame.test.js +0 -63
  109. package/test/data-channel-viewer-lifecycle.test.js +0 -193
  110. package/test/dead-torrent-handle.test.js +0 -52
  111. package/test/declared-rates.test.js +0 -116
  112. package/test/declared-tracks.test.js +0 -43
  113. package/test/decode-cost-fit.test.js +0 -202
  114. package/test/decode-cost.test.js +0 -475
  115. package/test/delivery-probe-field.test.js +0 -173
  116. package/test/delivery-probe.test.js +0 -382
  117. package/test/demand-register.test.js +0 -206
  118. package/test/dht-bootstrap.test.js +0 -72
  119. package/test/drain-threat.test.js +0 -65
  120. package/test/duplicate-add.test.js +0 -48
  121. package/test/encode-admission.test.js +0 -170
  122. package/test/encode-cost.test.js +0 -275
  123. package/test/encode-exit.test.js +0 -111
  124. package/test/encode-orchestrator.test.js +0 -426
  125. package/test/encode-plan-viewers.test.js +0 -725
  126. package/test/encode-plan.test.js +0 -545
  127. package/test/encode-run-state.test.js +0 -314
  128. package/test/encode-run.test.js +0 -248
  129. package/test/encode-slope.test.js +0 -46
  130. package/test/encoded-output.test.js +0 -39
  131. package/test/encoder-kinds.test.js +0 -122
  132. package/test/encoder-readings.test.js +0 -68
  133. package/test/episode-naming.test.js +0 -131
  134. package/test/every-claimant-is-told-its-share.test.js +0 -152
  135. package/test/failed-encoder-still-serves-what-exists.test.js +0 -354
  136. package/test/failed-start-breaker.test.js +0 -202
  137. package/test/fastest-wires.test.js +0 -143
  138. package/test/ffmpeg-stream-counts.test.js +0 -101
  139. package/test/file-edges.test.js +0 -191
  140. package/test/files-in-use.test.js +0 -34
  141. package/test/fixtures/audio-frame-boundary.json +0 -1
  142. package/test/fixtures/refused-audio-20261002.json +0 -10
  143. package/test/fmp4-presentation-clock.test.js +0 -148
  144. package/test/fmp4-self-contained.test.js +0 -80
  145. package/test/fmp4-timestamps.test.js +0 -140
  146. package/test/fmp4-track-completeness.test.js +0 -41
  147. package/test/format-keyed-outputs.test.js +0 -78
  148. package/test/health-metrics.test.js +0 -36
  149. package/test/held-read-ownership.test.js +0 -77
  150. package/test/held-request-width.test.js +0 -157
  151. package/test/helpers/encode-run.js +0 -248
  152. package/test/helpers/held-file.js +0 -77
  153. package/test/helpers/manager.js +0 -62
  154. package/test/helpers/matroska-file.js +0 -296
  155. package/test/helpers/output-spec.js +0 -63
  156. package/test/host-load.test.js +0 -68
  157. package/test/index-accuracy-is-about-the-file.test.js +0 -128
  158. package/test/init-compat.test.js +0 -219
  159. package/test/input-lost-quiets-the-plan.test.js +0 -261
  160. package/test/input-unavailable.test.js +0 -48
  161. package/test/installation-id.test.js +0 -39
  162. package/test/keeping-period.test.js +0 -83
  163. package/test/keyframe-index-accuracy.test.js +0 -188
  164. package/test/keyframe-table-budget.test.js +0 -123
  165. package/test/keyframes-belong-to-the-file.test.js +0 -249
  166. package/test/learned-median.test.js +0 -37
  167. package/test/letting-go-asks-who-is-watching.test.js +0 -191
  168. package/test/limit-row-by-area.test.js +0 -60
  169. package/test/link-budget.test.js +0 -111
  170. package/test/link-probe.test.js +0 -56
  171. package/test/local-encoding-data.test.js +0 -273
  172. package/test/logger-repeats.test.js +0 -134
  173. package/test/look-ahead-coverage.test.js +0 -56
  174. package/test/manager-surface.test.js +0 -291
  175. package/test/matroska-blocks.test.js +0 -0
  176. package/test/matroska-cluster-discovery.test.js +0 -323
  177. package/test/matroska-cues-track.test.js +0 -192
  178. package/test/media-time.test.js +0 -62
  179. package/test/memory-budget.test.js +0 -390
  180. package/test/move-cost.test.js +0 -216
  181. package/test/mp4-composition-times.test.js +0 -0
  182. package/test/mp4-subtitles.test.js +0 -173
  183. package/test/one-authority.test.js +0 -285
  184. package/test/one-budget.test.js +0 -149
  185. package/test/one-output-one-session.test.js +0 -273
  186. package/test/one-resource-divided.test.js +0 -185
  187. package/test/one-serving-output-per-height.test.js +0 -458
  188. package/test/orchestrator-wired.test.js +0 -207
  189. package/test/output-catalog.test.js +0 -39
  190. package/test/output-shape.test.js +0 -21
  191. package/test/output-spec.test.js +0 -251
  192. package/test/output-speed.test.js +0 -92
  193. package/test/outputs-being-prepared.test.js +0 -40
  194. package/test/packet-witness-ring.test.js +0 -236
  195. package/test/packet-witness.test.js +0 -148
  196. package/test/piece-completeness.test.js +0 -78
  197. package/test/piece-disk-store.test.js +0 -401
  198. package/test/piece-from-whole-file.test.js +0 -129
  199. package/test/piece-lru.test.js +0 -319
  200. package/test/piece-reader.test.js +0 -162
  201. package/test/piece-selection-offset.test.js +0 -85
  202. package/test/piece-store-eviction.test.js +0 -595
  203. package/test/piece-store-never-refuses.test.js +0 -153
  204. package/test/piece-store-reservations.test.js +0 -403
  205. package/test/piece-store-slow-disk.test.js +0 -159
  206. package/test/piece-tail.test.js +0 -97
  207. package/test/plan-host-timings.test.js +0 -68
  208. package/test/plan-is-steady.test.js +0 -225
  209. package/test/playback-readiness.test.js +0 -518
  210. package/test/playback-start-regressions.test.js +0 -101
  211. package/test/priority-map-download.test.js +0 -174
  212. package/test/priority-map-emptied.test.js +0 -204
  213. package/test/priority-map-per-output.test.js +0 -215
  214. package/test/priority-map.test.js +0 -163
  215. package/test/probe-wedge-certainty.test.js +0 -60
  216. package/test/produced-copy-choice.test.js +0 -213
  217. package/test/published-timeline.test.js +0 -83
  218. package/test/quality-offer.test.js +0 -160
  219. package/test/quality-variants.test.js +0 -1274
  220. package/test/rate-control-in-the-output.test.js +0 -181
  221. package/test/read-survives-withdrawal.test.js +0 -164
  222. package/test/read-window.test.js +0 -365
  223. package/test/reads-not-yet-arrived.test.js +0 -218
  224. package/test/resume-warm.test.js +0 -39
  225. package/test/run-graph-drift.test.js +0 -37
  226. package/test/run-inputs.test.js +0 -80
  227. package/test/run-intervals.test.js +0 -100
  228. package/test/run-position-follows-published-grid.test.js +0 -89
  229. package/test/run-progress.test.js +0 -64
  230. package/test/same-height-switch.test.js +0 -663
  231. package/test/seek-frees-the-viewers-requests.test.js +0 -217
  232. package/test/seek-landing.test.js +0 -109
  233. package/test/seek-target-not-superseded.test.js +0 -195
  234. package/test/segment-closed-on-both-branches.test.js +0 -114
  235. package/test/segment-cut-times.test.js +0 -63
  236. package/test/segment-demand.test.js +0 -80
  237. package/test/segment-hold-supersede.test.js +0 -91
  238. package/test/segment-judged-by-its-own-header.test.js +0 -222
  239. package/test/segment-serve-wiring.test.js +0 -481
  240. package/test/segment-store-eviction.test.js +0 -247
  241. package/test/segment-store.test.js +0 -352
  242. package/test/segment-true-start.test.js +0 -86
  243. package/test/segments-are-shared.test.js +0 -176
  244. package/test/send-chunk-bytes.test.js +0 -86
  245. package/test/serving-existing-output.test.js +0 -111
  246. package/test/serving-output.test.js +0 -102
  247. package/test/shared-piece-store.test.js +0 -310
  248. package/test/sidecar-naming.test.js +0 -142
  249. package/test/soundtrack-rate-bound.test.js +0 -289
  250. package/test/source-file.test.js +0 -133
  251. package/test/source-files-route.test.js +0 -113
  252. package/test/source-registry.test.js +0 -18
  253. package/test/split-a-stretch.test.js +0 -139
  254. package/test/stale-request-after-seek.test.js +0 -158
  255. package/test/startup-readings.test.js +0 -124
  256. package/test/stream-route.test.js +0 -237
  257. package/test/subtitle-cue-framing.test.js +0 -200
  258. package/test/subtitle-cue-source.test.js +0 -102
  259. package/test/subtitle-cue-walk.test.js +0 -385
  260. package/test/subtitle-cursor.test.js +0 -63
  261. package/test/subtitle-defaults.test.js +0 -118
  262. package/test/subtitle-language.test.js +0 -252
  263. package/test/subtitle-metadata.test.js +0 -15
  264. package/test/subtitle-plan-waits.test.js +0 -125
  265. package/test/subtitle-track-numbering.test.js +0 -391
  266. package/test/supply-margin.test.js +0 -190
  267. package/test/swarm-follows-readers.test.js +0 -174
  268. package/test/swarm-reach.test.js +0 -130
  269. package/test/swarm-selection.test.js +0 -220
  270. package/test/tail-duplication.test.js +0 -167
  271. package/test/the-channel-has-both-ends.test.js +0 -91
  272. package/test/the-torrent-thread-serves-bytes.test.js +0 -107
  273. package/test/thread-pool.test.js +0 -53
  274. package/test/timeline.test.js +0 -84
  275. package/test/torrent-contents.test.js +0 -235
  276. package/test/torrent-cost.test.js +0 -71
  277. package/test/torrent-files.test.js +0 -221
  278. package/test/torrent-naming.test.js +0 -255
  279. package/test/tracks-begin-together.test.js +0 -209
  280. package/test/tunnel-renewal.test.js +0 -167
  281. package/test/two-viewers-one-picture.test.js +0 -508
  282. package/test/upload-hurry.test.js +0 -186
  283. package/test/usrsctp-state.test.js +0 -131
  284. package/test/video-facts.test.js +0 -102
  285. package/test/viewer-assignments.test.js +0 -143
  286. package/test/viewer-outputs.test.js +0 -285
  287. package/test/viewer-presence.test.js +0 -116
  288. package/test/viewer-requests-admission.test.js +0 -92
  289. package/test/viewer-starts-on-map.test.js +0 -27
  290. package/test/viewer-states.test.js +0 -192
  291. package/test/viewer-subtitle-subscription.test.js +0 -96
  292. package/test/viewer.test.js +0 -139
  293. package/test/viewing-generations.test.js +0 -390
  294. package/test/visible-rung.test.js +0 -37
  295. package/test/wait-ledger.test.js +0 -100
  296. package/test/webrtc-shim.test.js +0 -149
  297. package/test/wedge-certainty.test.js +0 -131
  298. package/test/whole-file-held-read.test.js +0 -30
  299. package/test/withdraw-piece-claim.test.js +0 -212
  300. package/test/worker-channel.test.js +0 -158
@@ -1,143 +0,0 @@
1
- /**
2
- * @file Choosing who is asked for the piece a reader is blocked on.
3
- *
4
- * The measurement behind it (2026-08-17): a fivefold bandwidth surplus and 47
5
- * blocking waits in two minutes, on pieces a median of five peers already had.
6
- * A block belongs to one wire, so the read ends when the SLOWEST holder
7
- * delivers — which makes "who holds it" the thing worth deciding.
8
- */
9
-
10
- import assert from "node:assert/strict";
11
- import test from "node:test";
12
-
13
- import { askFastestWiresFor, canPlaceRequests, wiresForPiece } from "../services/torrent/worker/fastest-wires.js";
14
-
15
- /**
16
- * @param {{ speed: number, has?: boolean, choking?: boolean, destroyed?: boolean }} options
17
- * @returns {object}
18
- */
19
- function wire({ speed, has = true, choking = false, destroyed = false }) {
20
- return {
21
- destroyed,
22
- peerChoking: choking,
23
- peerPieces: { get: () => has },
24
- downloadSpeed: () => speed
25
- };
26
- }
27
-
28
- /**
29
- * @param {object[]} wires
30
- * @returns {{ torrent: object, asked: Array<{ speed: number, hotswap: boolean }> }}
31
- */
32
- function torrentWith(wires, { refuse = false } = {}) {
33
- /** @type {Array<{ speed: number, hotswap: boolean }>} */
34
- const asked = [];
35
- const torrent = {
36
- wires,
37
- _request(target, _index, hotswap) {
38
- asked.push({ speed: target.downloadSpeed(), hotswap });
39
- return !refuse;
40
- }
41
- };
42
- return { torrent, asked };
43
- }
44
-
45
- test("the fastest holders are asked first", () => {
46
- const { torrent, asked } = torrentWith([
47
- wire({ speed: 100_000 }),
48
- wire({ speed: 900_000 }),
49
- wire({ speed: 400_000 })
50
- ]);
51
-
52
- const result = askFastestWiresFor(torrent, 42);
53
-
54
- assert.deepEqual(
55
- asked.map((entry) => entry.speed),
56
- [900_000, 400_000, 100_000],
57
- "fastest first — the read ends when the slowest holder delivers"
58
- );
59
- assert.equal(result.asked, 3);
60
- assert.equal(result.fastestBytesPerSecond, 900_000);
61
- });
62
-
63
- test("hotswap is always on, because that is the whole point", () => {
64
- // The reader is blocked precisely because every block is reserved and one of
65
- // them sits with a slow wire. Without hotswap the library answers "nothing to
66
- // reserve" and the lever does nothing at all.
67
- const { torrent, asked } = torrentWith([wire({ speed: 10 })]);
68
- askFastestWiresFor(torrent, 7);
69
- assert.deepEqual(asked.map((entry) => entry.hotswap), [true]);
70
- });
71
-
72
- test("a wire that cannot deliver is not asked", () => {
73
- const { torrent, asked } = torrentWith([
74
- wire({ speed: 900_000, choking: true }),
75
- wire({ speed: 800_000, has: false }),
76
- wire({ speed: 700_000, destroyed: true }),
77
- wire({ speed: 1_000 })
78
- ]);
79
-
80
- const result = askFastestWiresFor(torrent, 3);
81
-
82
- assert.equal(asked.length, 1, "a choking, an absent and a dead wire are all unusable");
83
- assert.equal(result.considered, 1);
84
- });
85
-
86
- test("a refusal is counted as a refusal", () => {
87
- // The library refuses when a wire's pipeline is full or nothing can be
88
- // reserved even with hotswap. That is information: the piece is waiting on
89
- // the wire, not on the picker.
90
- const { torrent } = torrentWith([wire({ speed: 500 }), wire({ speed: 400 })], { refuse: true });
91
- const result = askFastestWiresFor(torrent, 11);
92
- assert.equal(result.asked, 0);
93
- assert.equal(result.considered, 2);
94
- // Refused, but not because every block was spoken for — this stub has no
95
- // reservations at all. The two reasons are different and only the second is
96
- // the one WebTorrent's displacement thresholds decide.
97
- assert.equal(result.refusedWhileReserved, 0);
98
- });
99
-
100
- test("a refusal with every block already reserved is counted separately", () => {
101
- // The interesting refusal: a fast peer we picked was turned away because
102
- // every block belongs to somebody else and displacement did not happen. The
103
- // thresholds that decide it are constants in the library — the asker above
104
- // 16 KB/s, the holder below 48 KB/s and twice as slow — so a holder at
105
- // 50 KB/s is never displaced however long the piece has waited. This number
106
- // is what would justify replacing that rule.
107
- const { torrent } = torrentWith([wire({ speed: 500 })], { refuse: true });
108
- torrent.pieces = [];
109
- torrent.pieces[11] = { reserve: () => -1, cancel: () => undefined };
110
- const result = askFastestWiresFor(torrent, 11);
111
- assert.equal(result.asked, 0);
112
- assert.equal(result.refusedWhileReserved, 1);
113
- });
114
-
115
- test("a build without the request entry is reported, not silently skipped", () => {
116
- assert.equal(canPlaceRequests({ wires: [] }), false);
117
- assert.equal(canPlaceRequests({ wires: [], _request: () => true }), true);
118
- const result = askFastestWiresFor({ wires: [wire({ speed: 1 })] }, 1);
119
- assert.deepEqual(
120
- result,
121
- { asked: 0, refusedWhileReserved: 0, attempted: 0, considered: 0, fastestBytesPerSecond: 0 }
122
- );
123
- });
124
-
125
- test("a wire that cannot say how fast it is ranks last rather than throwing", () => {
126
- const mute = wire({ speed: 0 });
127
- mute.downloadSpeed = () => {
128
- throw new Error("destroyed");
129
- };
130
- const ranked = wiresForPiece({ wires: [mute, wire({ speed: 5 })] }, 0);
131
- assert.equal(ranked.length, 2);
132
- assert.equal(ranked[0].downloadSpeed(), 5);
133
- });
134
-
135
- test("a throw from the library is raised, never swallowed", () => {
136
- const torrent = {
137
- wires: [wire({ speed: 1 })],
138
- _request() {
139
- throw new Error("internal");
140
- }
141
- };
142
- assert.throws(() => askFastestWiresFor(torrent, 5), /could not place a request for piece 5/);
143
- });
@@ -1,101 +0,0 @@
1
- /**
2
- * @file What the source said it holds, read from ffmpeg's own banner.
3
- *
4
- * The reading exists for one failure: every `-map` this proxy builds ends in
5
- * `?`, so ffmpeg drops a mapping for a stream that is not there and says
6
- * nothing. Map every stream of a run away that way and the output has nothing
7
- * in it — `Output file does not contain any stream`, exit 255. Three sessions
8
- * died that way on 2026-08-26 and the log held only the code.
9
- */
10
-
11
- import assert from "node:assert/strict";
12
- import test from "node:test";
13
-
14
- import { parseFfmpegStreamCounts } from "../services/media/ffmpeg-banner.js";
15
-
16
- const FIELD_BANNER = `
17
- Input #0, matroska,webm, from 'http://127.0.0.1:9090/stream?sourceKey=b3f08efc':
18
- Duration: 01:32:39.00, start: 0.000000, bitrate: 9354 kb/s
19
- Stream #0:0: Video: h264 (High), yuv420p(progressive), 1920x1080, 23.98 fps
20
- Stream #0:1(rus): Audio: ac3, 48000 Hz, 5.1, fltp, 640 kb/s
21
- Stream #0:2(eng): Audio: aac, 48000 Hz, stereo, fltp, 128 kb/s
22
- Stream #0:3(rus): Subtitle: subrip
23
- Stream #0:4(eng): Subtitle: hdmv_pgs_subtitle
24
- `;
25
-
26
- test("each stream is counted under the type ffmpeg names it", () => {
27
- assert.deepEqual(parseFfmpegStreamCounts(FIELD_BANNER), {
28
- video: 1,
29
- audio: 2,
30
- subtitle: 2,
31
- other: 0
32
- });
33
- });
34
-
35
- test("a file with no audio is what makes an audio-only run produce nothing", () => {
36
- // This is the shape the diagnostic is for: the rendition asks for `0:a:0?`,
37
- // the file has no audio at all, the mapping is dropped in silence and the
38
- // segment muxer is handed an output with no streams.
39
- const counts = parseFfmpegStreamCounts(`
40
- Stream #0:0: Video: hevc (Main 10), yuv420p10le, 3840x2160, 23.98 fps
41
- `);
42
- assert.equal(counts.audio, 0);
43
- assert.equal(counts.video, 1);
44
- });
45
-
46
- test("an audio index past the end of the list is OUR fault, and the counts say so", () => {
47
- // A viewer picking the third dub on a file carrying one: `0:a:2?` drops, and
48
- // the count is what separates that from a source that delivered nothing.
49
- const counts = parseFfmpegStreamCounts(FIELD_BANNER);
50
- const askedFor = 4;
51
- assert.ok(askedFor >= counts.audio, "the request is outside what the file holds");
52
- });
53
-
54
- test("an attached cover image is counted as video, because that is what would be mapped", () => {
55
- const counts = parseFfmpegStreamCounts(`
56
- Stream #0:0: Audio: mp3, 44100 Hz, stereo, fltp, 320 kb/s
57
- Stream #0:1: Video: mjpeg (Baseline), yuvj420p(pc), 600x600 (attached pic)
58
- `);
59
- assert.equal(counts.video, 1);
60
- assert.equal(counts.audio, 1);
61
- });
62
-
63
- test("a banner carrying no stream lines is not the same as a file with no streams", () => {
64
- // Null says "not recorded"; zeroes would claim a measurement nobody took.
65
- assert.equal(parseFfmpegStreamCounts("Duration: 00:10:00.00, start: 0.000000"), null);
66
- assert.equal(parseFfmpegStreamCounts(""), null);
67
- assert.equal(parseFfmpegStreamCounts(undefined), null);
68
- });
69
-
70
- test("a stream carrying metadata in brackets is still read", () => {
71
- // MP4 writes `Stream #0:1[0x2](eng)`, which the earlier shape of this regex
72
- // would have skipped.
73
- const counts = parseFfmpegStreamCounts(`
74
- Stream #0:0[0x1](und): Video: h264 (avc1), yuv420p, 1280x720, 24 fps
75
- Stream #0:1[0x2](eng): Audio: aac (mp4a), 48000 Hz, stereo, fltp
76
- `);
77
- assert.deepEqual(counts, { video: 1, audio: 1, subtitle: 0, other: 0 });
78
- });
79
-
80
- test("only the input's streams are counted, not the output ffmpeg describes after them", () => {
81
- // The planner probes with `-i <file> -t 0.1 -f null -`, so the banner goes on
82
- // to describe what it maps and the null output it writes. Counted whole, this
83
- // field file (one picture, one soundtrack, one subtitle, one font) read as
84
- // "2 video, 2 audio, 1 subtitle, 3 other" on 2026-10-04 — which would make a
85
- // second soundtrack look present when it is not.
86
- const banner = `
87
- Input #0, matroska,webm, from 'http://127.0.0.1:9090/stream?sourceKey=torrent%3A84acab5f&fileIndex=35':
88
- Duration: 00:23:41.44, start: 0.000000, bitrate: 3195 kb/s
89
- Stream #0:0: Video: h264 (High), yuv420p(progressive), 1920x1080 [SAR 1:1 DAR 16:9], 23.81 fps, 23.81 tbr, 1k tbn (default)
90
- Stream #0:1(jpn): Audio: aac (LC), 44100 Hz, stereo, fltp (default)
91
- Stream #0:2(eng): Subtitle: ass (ssa) (default)
92
- Stream #0:3: Attachment: ttf
93
- Stream mapping:
94
- Stream #0:0 -> #0:0 (h264 (native) -> wrapped_avframe (native))
95
- Stream #0:1 -> #0:1 (aac (native) -> pcm_s16le (native))
96
- Output #0, null, to 'pipe:':
97
- Stream #0:0: Video: wrapped_avframe, yuv420p(progressive), 1920x1080 [SAR 1:1 DAR 16:9], q=2-31, 200 kb/s, 23.81 fps, 23.81 tbn (default)
98
- Stream #0:1(jpn): Audio: pcm_s16le, 44100 Hz, stereo, s16, 1411 kb/s (default)
99
- `;
100
- assert.deepEqual(parseFfmpegStreamCounts(banner), { video: 1, audio: 1, subtitle: 1, other: 1 });
101
- });
@@ -1,191 +0,0 @@
1
- /**
2
- * @file THE TWO ENDS OF A FILE ARE THE FILE'S, NOT A READ'S.
3
- *
4
- * A container keeps its directory at one end or the other: `ftyp` and an EBML
5
- * header at the front, and for an MP4 that was not written for streaming the
6
- * `moov` at the very back. Nothing can be read of such a file until they have
7
- * arrived, and they go on being wanted for as long as it is open — a player
8
- * asks for the file's shape again at every seek.
9
- *
10
- * Until 2.83.4 they were wanted by nobody. The prefetch that fetches them is an
11
- * ordinary read, and a read withdraws what it states the moment it finishes, so
12
- * the ends of an open film were held by nothing at all once the codec probe was
13
- * done.
14
- */
15
-
16
- import test from "node:test";
17
- import assert from "node:assert/strict";
18
- import { TorrentPool, stateFileEdges, withdrawFileEdges } from "../services/torrent/torrent-pool.js";
19
- import { demandFor, forgetTorrent } from "../services/torrent/download/registry.js";
20
- import { Urgency } from "../services/torrent/demand/index.js";
21
-
22
- /** A torrent of two files, of which the second is the film. */
23
- function torrentOf({ length = 1_000_000 } = {}) {
24
- return {
25
- infoHash: `hash-${Math.random().toString(36).slice(2)}`,
26
- pieceLength: 1024,
27
- files: [
28
- { offset: 0, length: 4096, name: "notes.nfo" },
29
- { offset: 4096, length, name: "film.mkv" }
30
- ],
31
- _selections: { _items: [] },
32
- _select() {},
33
- _deselect() {},
34
- critical() {}
35
- };
36
- }
37
-
38
- /**
39
- * @param {object} torrent
40
- * @returns {object[]}
41
- */
42
- function edges(torrent) {
43
- return demandFor(torrent)
44
- .register.windows()
45
- .filter((one) => String(one.claimant).startsWith("file-edges:"));
46
- }
47
-
48
- test("one byte is claimed at each end, which is one piece at each end", () => {
49
- const torrent = torrentOf({ length: 1_000_000 });
50
- try {
51
- assert.equal(stateFileEdges(torrent, 1, Urgency.TAIL), true);
52
-
53
- const stated = edges(torrent);
54
- assert.equal(stated.length, 2);
55
- const head = stated.find((one) => String(one.claimant).endsWith(":head"));
56
- const tail = stated.find((one) => String(one.claimant).endsWith(":tail"));
57
- // File-relative, like everything else stated about a file: the claim says
58
- // "the first byte of this file" and "its last", and what a byte costs to
59
- // fetch — a whole piece — is the swarm's business and nobody else's.
60
- assert.deepEqual([head.byteStart, head.byteEnd], [0, 0]);
61
- assert.deepEqual([tail.byteStart, tail.byteEnd], [999_999, 999_999]);
62
- assert.equal(head.fileIndex, 1);
63
- assert.equal(tail.fileIndex, 1);
64
- } finally {
65
- forgetTorrent(torrent);
66
- }
67
- });
68
-
69
- test("while somebody is waiting for them they are urgent, and afterwards they are kept", () => {
70
- const torrent = torrentOf();
71
- try {
72
- // The playback plan cannot answer until the file has said what is in it,
73
- // and a person is watching a loading screen for as long as that takes.
74
- stateFileEdges(torrent, 1, Urgency.NEAR);
75
- assert.deepEqual(
76
- edges(torrent).map((one) => one.urgency),
77
- [Urgency.NEAR, Urgency.NEAR]
78
- );
79
-
80
- // Once it has answered, nobody is waiting — but a seek will want them
81
- // again, so they stay stated at the level of something nobody is waiting
82
- // for. Re-stating replaces; it does not pile a second claim on the first.
83
- stateFileEdges(torrent, 1, Urgency.TAIL, { lower: true });
84
- assert.equal(edges(torrent).length, 2);
85
- assert.deepEqual(
86
- edges(torrent).map((one) => one.urgency),
87
- [Urgency.TAIL, Urgency.TAIL]
88
- );
89
- } finally {
90
- forgetTorrent(torrent);
91
- }
92
- });
93
-
94
- test("each file of a torrent has its own ends", () => {
95
- const torrent = torrentOf();
96
- try {
97
- stateFileEdges(torrent, 0, Urgency.TAIL);
98
- stateFileEdges(torrent, 1, Urgency.NEAR);
99
- assert.equal(edges(torrent).length, 4);
100
-
101
- withdrawFileEdges(torrent, 0);
102
- assert.deepEqual(
103
- edges(torrent).map((one) => one.fileIndex),
104
- [1, 1]
105
- );
106
- } finally {
107
- forgetTorrent(torrent);
108
- }
109
- });
110
-
111
- test("a file of unknown length has no ends to claim", () => {
112
- const torrent = torrentOf();
113
- try {
114
- assert.equal(stateFileEdges(torrent, 7, Urgency.TAIL), false);
115
- assert.equal(stateFileEdges({ files: [{ length: 0 }] }, 0, Urgency.TAIL), false);
116
- assert.equal(edges(torrent).length, 0);
117
- } finally {
118
- forgetTorrent(torrent);
119
- }
120
- });
121
-
122
- test("a map with nothing in it takes the ends back too", () => {
123
- const torrent = torrentOf();
124
- const applyPriorityMap = TorrentPool.prototype.applyPriorityMap;
125
- try {
126
- stateFileEdges(torrent, 1, Urgency.TAIL);
127
- applyPriorityMap.call(null, torrent, 1, [{ from: 0, to: 600, priority: 100 }], 600);
128
- assert.equal(edges(torrent).length, 2);
129
-
130
- // Nobody wants this file any more, and its ends are kept only for as long
131
- // as it is open.
132
- applyPriorityMap.call(null, torrent, 1, [], 600);
133
- assert.equal(edges(torrent).length, 0);
134
- assert.deepEqual(demandFor(torrent).register.windows(), []);
135
- } finally {
136
- forgetTorrent(torrent);
137
- }
138
- });
139
-
140
- test("another file's departure leaves these ends alone", () => {
141
- const torrent = torrentOf();
142
- const applyPriorityMap = TorrentPool.prototype.applyPriorityMap;
143
- try {
144
- stateFileEdges(torrent, 0, Urgency.TAIL);
145
- stateFileEdges(torrent, 1, Urgency.TAIL);
146
-
147
- applyPriorityMap.call(null, torrent, 0, [], 600);
148
-
149
- assert.deepEqual(
150
- edges(torrent).map((one) => one.fileIndex),
151
- [1, 1]
152
- );
153
- } finally {
154
- forgetTorrent(torrent);
155
- }
156
- });
157
-
158
- test("a warm-up cannot take the urgency away from a plan that is waiting", () => {
159
- // The two callers of the same ends: a playback plan, which somebody is
160
- // watching a loading screen for, and a warm-up, which by its whole purpose
161
- // nobody is waiting for. The warm-up fires off the files beside the picture
162
- // without awaiting them, so it can arrive second — and lowering the plan's
163
- // level there would put a person behind a film somebody else is watching.
164
- const torrent = torrentOf();
165
- try {
166
- stateFileEdges(torrent, 1, Urgency.NEAR);
167
- stateFileEdges(torrent, 1, Urgency.TAIL);
168
-
169
- assert.deepEqual(
170
- edges(torrent).map((one) => one.urgency),
171
- [Urgency.NEAR, Urgency.NEAR]
172
- );
173
- } finally {
174
- forgetTorrent(torrent);
175
- }
176
- });
177
-
178
- test("the read that is over is what puts them back down", () => {
179
- const torrent = torrentOf();
180
- try {
181
- stateFileEdges(torrent, 1, Urgency.NEAR);
182
- stateFileEdges(torrent, 1, Urgency.TAIL, { lower: true });
183
-
184
- assert.deepEqual(
185
- edges(torrent).map((one) => one.urgency),
186
- [Urgency.TAIL, Urgency.TAIL]
187
- );
188
- } finally {
189
- forgetTorrent(torrent);
190
- }
191
- });
@@ -1,34 +0,0 @@
1
- /**
2
- * @file Which files of a torrent are in use for the whole-file sweep. Pure:
3
- * no torrent is started (see services/torrent/worker/files-in-use.js).
4
- */
5
- import test from "node:test";
6
- import assert from "node:assert/strict";
7
- import { filesInUse } from "../services/torrent/worker/files-in-use.js";
8
-
9
- test("a file being read is in use though nobody states a need for it", () => {
10
- // Field 2026-10-04: every piece was here, so the read waited for nothing and
11
- // stated nothing, and the torrent was removed under it.
12
- const torrent = {};
13
- const used = filesInUse({ torrent, windows: [], openReads: [{ torrent, fileIndex: 0 }] });
14
- assert.deepEqual([...used], [0]);
15
- });
16
-
17
- test("a read of another torrent does not count", () => {
18
- const used = filesInUse({ torrent: {}, windows: [], openReads: [{ torrent: {}, fileIndex: 0 }] });
19
- assert.equal(used.size, 0);
20
- });
21
-
22
- test("stated needs count, and the fills that read nothing do not", () => {
23
- const used = filesInUse({
24
- torrent: {},
25
- windows: [
26
- { claimant: "reader:7", fileIndex: 2 },
27
- { claimant: "file-edges:1", fileIndex: 1 },
28
- { claimant: "torrent-fill:x", fileIndex: 3 },
29
- { claimant: "background-fill:x", fileIndex: 4 }
30
- ],
31
- openReads: []
32
- });
33
- assert.deepEqual([...used], [2]);
34
- });
@@ -1 +0,0 @@
1
- {"description":"MP4 metadata boxes captured from adjacent AAC segments. Media payloads are omitted.","files":{"init.mp4":"AAAAHGZ0eXBpc281AAACAGlzbzVpc282bXA0MQAAAvRtb292AAAAbG12aGQAAAAAAAAAAAAAAAAAAAPoAAAAAAABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAB9nRyYWsAAABcdGtoZAAAAAMAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAQEAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAACRlZHRzAAAAHGVsc3QAAAAAAAAAAQAAAAAAAAAAAAEAAAAAAVttZGlhAAAAIG1kaGQAAAAAAAAAAAAAAAAAALuAAAAAAFXEAAAAAAAtaGRscgAAAAAAAAAAc291bgAAAAAAAAAAAAAAAFNvdW5kSGFuZGxlcgAAAAEGbWluZgAAABBzbWhkAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAADKc3RibAAAAH5zdHNkAAAAAAAAAAEAAABubXA0YQAAAAAAAAABAAAAAAAAAAAAAgAQAAAAALuAAAAAAAA2ZXNkcwAAAAADgICAJQABAASAgIAXQBUAAAAAAfQAAAH0AAWAgIAFEZBW5QAGgICAAQIAAAAUYnRydAAAAAAAAfQAAAH0AAAAABBzdHRzAAAAAAAAAAAAAAAQc3RzYwAAAAAAAAAAAAAAFHN0c3oAAAAAAAAAAAAAAAAAAAAQc3RjbwAAAAAAAAAAAAAAE3VkdGEAAAALbmFtZVJVUwAAAChtdmV4AAAAIHRyZXgAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAABidWR0YQAAAFptZXRhAAAAAAAAACFoZGxyAAAAAAAAAABtZGlyYXBwbAAAAAAAAAAAAAAAAC1pbHN0AAAAJal0b28AAAAdZGF0YQAAAAEAAAAATGF2ZjYyLjEyLjEwMg==","segment-00171.mp4":"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","segment-00172.mp4":"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"},"browserBufferedRanges":[[1782.997,1793.428999],[1782.997,1803.827333]]}
@@ -1,10 +0,0 @@
1
- {
2
- "source": "Headers captured on Home Assistant, proxy 2.89.6, 2026-10-02; media payload omitted.",
3
- "files": {
4
- "making-0-00013.mp4": "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",
5
- "making-0-00090.mp4": "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",
6
- "repair-13.mp4": "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",
7
- "repair-14.mp4": "AAADAG1vb3YAAABsbXZoZAAAAAAAAAAAAAAAAAAAA+gAAAAAAAEAAAEAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAICdHJhawAAAFx0a2hkAAAAAwAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAABAQAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAMGVkdHMAAAAoZWxzdAAAAAAAAAACAAE91P////8AAQAAAAAAAAAAAAAAAQAAAAABW21kaWEAAAAgbWRoZAAAAAAAAAAAAAAAAAAAu4AAAAAAVcQAAAAAAC1oZGxyAAAAAAAAAABzb3VuAAAAAAAAAAAAAAAAU291bmRIYW5kbGVyAAAAAQZtaW5mAAAAEHNtaGQAAAAAAAAAAAAAACRkaW5mAAAAHGRyZWYAAAAAAAAAAQAAAAx1cmwgAAAAAQAAAMpzdGJsAAAAfnN0c2QAAAAAAAAAAQAAAG5tcDRhAAAAAAAAAAEAAAAAAAAAAAACABAAAAAAu4AAAAAAADZlc2RzAAAAAAOAgIAlAAEABICAgBdAFQAAAAAB9AAAAfQABYCAgAURkFblAAaAgIABAgAAABRidHJ0AAAAAAAB9AAAAfQAAAAAEHN0dHMAAAAAAAAAAAAAABBzdHNjAAAAAAAAAAAAAAAUc3RzegAAAAAAAAAAAAAAAAAAABBzdGNvAAAAAAAAAAAAAAATdWR0YQAAAAtuYW1lUlVTAAAAKG12ZXgAAAAgdHJleAAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAGJ1ZHRhAAAAWm1ldGEAAAAAAAAAIWhkbHIAAAAAAAAAAG1kaXJhcHBsAAAAAAAAAAAAAAAALWlsc3QAAAAlqXRvbwAAAB1kYXRhAAAAAQAAAABMYXZmNjIuMTIuMTAyAAAAcG1vb2YAAAAQbWZoZAAAAAAAAAABAAAAWHRyYWYAAAAcdGZoZAACADgAAAABAAAEAAAAAU8CAAAAAAAAFHRmZHQBAAAAAAAAAAAAAAAAAAAgdHJ1bgAAAgEAAAADAAAAeAAAAU8AAAFZAAABag==",
8
- "segment-00014.mp4": "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"
9
- }
10
- }