@torrent-tv/proxy 2.87.3 → 2.87.4
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/.claude/commands/opsx/apply.md +155 -155
- package/.claude/commands/opsx/archive.md +160 -160
- package/.claude/commands/opsx/explore.md +174 -174
- package/.claude/commands/opsx/propose.md +109 -109
- package/.claude/commands/opsx/sync.md +143 -143
- package/.claude/skills/openspec-apply-change/SKILL.md +159 -159
- package/.claude/skills/openspec-archive-change/SKILL.md +117 -117
- package/.claude/skills/openspec-explore/SKILL.md +289 -289
- package/.claude/skills/openspec-propose/SKILL.md +113 -113
- package/.claude/skills/openspec-sync-specs/SKILL.md +147 -147
- package/.dockerignore +5 -5
- package/AGENTS.md +322 -322
- package/CHANGELOG.md +2157 -2153
- package/Dockerfile +30 -30
- package/LICENSE +16 -16
- package/README.md +369 -369
- package/assets/calibration/NOTICE.md +100 -100
- package/assets/diagnostics/sctpstate.gdb +31 -31
- package/bin/cli.js +684 -684
- package/bin/thread-pool.js +30 -30
- package/biome.json +411 -411
- package/docs/browser-proxy-contract.md +94 -94
- package/docs/container-architecture.md +218 -218
- package/docs/download-architecture.md +261 -261
- package/docs/encode-architecture.md +729 -729
- package/docs/encode-run-state.md +96 -96
- package/docs/logs.md +97 -97
- package/docs/socks5-egress-research.md +114 -114
- package/docs/storage-architecture.md +267 -267
- package/knip.json +14 -14
- package/openspec/changes/adaptive-bitrate/design.md +92 -92
- package/openspec/changes/adaptive-bitrate/proposal.md +56 -56
- package/openspec/changes/adaptive-bitrate/specs/adaptive-bitrate/spec.md +51 -51
- package/openspec/changes/adaptive-bitrate/tasks.md +40 -40
- package/openspec/changes/binary-distribution/.openspec.yaml +2 -2
- package/openspec/changes/binary-distribution/proposal.md +53 -53
- package/openspec/changes/chunked-request-bodies/design.md +81 -81
- package/openspec/changes/chunked-request-bodies/proposal.md +53 -53
- package/openspec/changes/chunked-request-bodies/specs/chunked-request-bodies/spec.md +30 -30
- package/openspec/changes/chunked-request-bodies/tasks.md +40 -40
- package/openspec/changes/cold-start/design.md +98 -98
- package/openspec/changes/cold-start/proposal.md +62 -62
- package/openspec/changes/cold-start/specs/cold-start/spec.md +47 -47
- package/openspec/changes/cold-start/tasks.md +48 -48
- package/openspec/changes/disk-cap/.openspec.yaml +2 -2
- package/openspec/changes/disk-cap/proposal.md +37 -37
- package/openspec/changes/disk-cap/specs/disk-cap/spec.md +25 -25
- package/openspec/changes/disk-cap/tasks.md +19 -19
- package/openspec/changes/proxy-observability/.openspec.yaml +2 -2
- package/openspec/changes/proxy-observability/design.md +38 -38
- package/openspec/changes/proxy-observability/proposal.md +49 -49
- package/openspec/changes/proxy-observability/specs/observability/spec.md +33 -33
- package/openspec/changes/proxy-observability/tasks.md +21 -21
- package/openspec/changes/seek-debounce/design.md +118 -118
- package/openspec/changes/seek-debounce/proposal.md +59 -59
- package/openspec/changes/seek-debounce/specs/seek-debounce/spec.md +41 -41
- package/openspec/changes/seek-debounce/tasks.md +33 -33
- package/openspec/changes/subtitle-language/.openspec.yaml +2 -2
- package/openspec/changes/subtitle-language/proposal.md +50 -50
- package/openspec/changes/subtitle-language/specs/subtitle-language/spec.md +34 -34
- package/openspec/changes/subtitle-language/tasks.md +26 -26
- package/openspec/changes/track-selection/.openspec.yaml +2 -2
- package/openspec/changes/track-selection/design.md +47 -47
- package/openspec/changes/track-selection/proposal.md +45 -45
- package/openspec/changes/track-selection/specs/track-selection/spec.md +41 -41
- package/openspec/changes/track-selection/tasks.md +20 -20
- package/openspec/changes/transcode-quality/.openspec.yaml +2 -2
- package/openspec/changes/transcode-quality/proposal.md +51 -51
- package/openspec/changes/transcode-quality/specs/transcode-quality/spec.md +139 -139
- package/openspec/changes/transcode-quality/tasks.md +63 -63
- package/openspec/config.yaml +38 -38
- package/package.json +50 -50
- package/public/.well-known/appspecific/com.chrome.devtools.json +1 -1
- package/research/delivery-wedge-stand-ladder-2026-09-13.md +329 -329
- package/research/double-spawn-2026-09-10.md +171 -171
- package/research/handover-2026-09-13.md +209 -209
- package/research/handover-reader-claims-removal-2026-09-12.md +166 -166
- package/research/piece-withdrawn-but-still-claimed-2026-09-12.md +175 -175
- package/research/preset-benchmark-size-scaling-2026-08-22.md +55 -55
- package/research/priority-map-is-the-truth-2026-09-12.md +225 -225
- package/routes/api/client-logs/post.js +86 -86
- package/routes/api/delivery-sink/get.js +72 -72
- package/routes/api/link-probe/get.js +51 -51
- package/routes/api/playback-plan/post.js +53 -53
- package/routes/api/sources/files/get.js +100 -100
- package/routes/api/sources/post.js +36 -36
- package/routes/api/sources/stats/get.js +132 -132
- package/routes/api/sources/warm/post.js +217 -181
- package/routes/api/subtitles/get.js +230 -230
- package/routes/api/transcode-sessions/fragment-far/post.js +60 -60
- package/routes/api/transcode-sessions/net-report/post.js +129 -129
- package/routes/api/transcode-sessions/post.js +160 -160
- package/routes/api/transcode-sessions/progress/get.js +27 -27
- package/routes/api/transcode-sessions/release/post.js +41 -41
- package/routes/api/transcode-sessions/seek/post.js +63 -63
- package/routes/health/get.js +13 -13
- package/routes/healthz/get.js +13 -13
- package/routes/stream/get.js +361 -361
- package/routes/transcode/audio-file/get.js +64 -64
- package/routes/transcode/audio-warm/get.js +84 -84
- package/routes/transcode/session-file/get.js +52 -52
- package/routes/transcode/variant-file/get.js +95 -95
- package/routes/transcode/variant-warm/get.js +84 -84
- package/scripts/render-run-graph.js +173 -173
- package/server.js +721 -720
- package/services/encode/CoverageMap.js +428 -428
- package/services/encode/CushionReport.js +345 -345
- package/services/encode/EncodeAdmission.js +332 -332
- package/services/encode/EncodeOrchestrator.js +1217 -1217
- package/services/encode/EncodePlan.js +1366 -1366
- package/services/encode/EncodeRun.js +776 -776
- package/services/encode/EncodeRuns.js +1229 -1229
- package/services/encode/Encoder.js +142 -142
- package/services/encode/EncoderSelection.js +38 -38
- package/services/encode/LocalObservations.js +359 -359
- package/services/encode/NvencEncoder.js +50 -50
- package/services/encode/OutputOpening.js +755 -755
- package/services/encode/OutputTimes.js +579 -579
- package/services/encode/QsvEncoder.js +58 -58
- package/services/encode/Renditions.js +2162 -2162
- package/services/encode/RunProgress.js +84 -84
- package/services/encode/SegmentDemand.js +95 -95
- package/services/encode/SoftwareEncoder.js +121 -121
- package/services/encode/V4l2m2mEncoder.js +53 -53
- package/services/encode/VaapiEncoder.js +66 -66
- package/services/encode/args.js +416 -416
- package/services/encode/available-share.js +89 -89
- package/services/encode/calibration.js +361 -361
- package/services/encode/contention.js +128 -128
- package/services/encode/decode-cost-fit.js +363 -363
- package/services/encode/encode-exit.js +114 -114
- package/services/encode/encode-run-state.js +551 -551
- package/services/encode/encoder-readings.js +45 -45
- package/services/encode/fingerprint.js +322 -322
- package/services/encode/host-load.js +174 -174
- package/services/encode/hwaccel.js +1894 -1894
- package/services/encode/index.js +9 -9
- package/services/encode/learned-median.js +90 -90
- package/services/encode/output/EncodedOutput.js +57 -57
- package/services/encode/output/Output.js +131 -131
- package/services/encode/output/OutputCatalog.js +414 -414
- package/services/encode/output/OutputSpec.js +390 -390
- package/services/encode/output/Timeline.js +308 -308
- package/services/encode/output/cut-grid.js +107 -107
- package/services/encode/output/index.js +20 -20
- package/services/encode/output/ladder.js +32 -32
- package/services/encode/output/playlists.js +284 -284
- package/services/encode/output/rates.js +125 -125
- package/services/encode/output-key-format.js +21 -21
- package/services/encode/quality/EncodeCost.js +1083 -1083
- package/services/encode/quality/HostLoad.js +525 -525
- package/services/encode/quality/HostTimings.js +249 -249
- package/services/encode/quality/OutputQualityState.js +28 -28
- package/services/encode/quality/QualityController.js +935 -935
- package/services/encode/quality/QualityOffer.js +416 -416
- package/services/encode/quality/drain-threat.js +47 -47
- package/services/encode/quality/link-budget.js +254 -254
- package/services/encode/quality/output-format.js +266 -266
- package/services/encode/quality/serving-output.js +216 -216
- package/services/encode/quality/visible-rung.js +57 -57
- package/services/encode/run-budget.js +97 -97
- package/services/encode/run-command.js +747 -747
- package/services/encode/run-costs.js +155 -155
- package/services/encode/run-inputs.js +42 -42
- package/services/encode/segment-formats/fmp4.js +437 -437
- package/services/encode/segment-formats/index.js +77 -77
- package/services/encode/segment-formats/init-compat.js +324 -324
- package/services/encode/segment-formats/mp4-boxes.js +311 -311
- package/services/encode/segment-formats/mpegts.js +134 -134
- package/services/encode/start-stop-cost.js +178 -178
- package/services/encode/throughput.js +97 -97
- package/services/encode/torrent-cost.js +88 -88
- package/services/identity/installation-id.js +38 -38
- package/services/media/ContainerOrchestrator.js +149 -149
- package/services/media/KeyframeTables.js +316 -316
- package/services/media/SourceFile.js +298 -298
- package/services/media/SubtitleCues.js +520 -520
- package/services/media/SubtitleOrchestrator.js +167 -167
- package/services/media/audio-inventory.js +212 -212
- package/services/media/container/AviContainer.js +266 -266
- package/services/media/container/Container.js +603 -603
- package/services/media/container/ContainerFactory.js +56 -56
- package/services/media/container/KeyframeTable.js +272 -272
- package/services/media/container/MatroskaContainer.js +1494 -1494
- package/services/media/container/Mp4Container.js +1244 -1244
- package/services/media/container/SubtitleFileContainer.js +322 -322
- package/services/media/container/ebml-reader.js +159 -159
- package/services/media/ffmpeg-banner.js +260 -260
- package/services/media/keyframe-probe.js +157 -157
- package/services/media/media-info-probe.js +93 -93
- package/services/media/playback-planner.js +856 -848
- package/services/media/tracks/AudioTrack.js +131 -131
- package/services/media/tracks/ContainerTrack.js +75 -75
- package/services/media/tracks/ImageSubtitleTrack.js +19 -19
- package/services/media/tracks/SubtitleTrack.js +38 -38
- package/services/media/tracks/TextSubtitleTrack.js +287 -287
- package/services/media/tracks/VideoTrack.js +33 -33
- package/services/media/tracks/index.js +15 -15
- package/services/media/tracks/language-detect.js +174 -174
- package/services/server/OutputLifecycle.js +418 -418
- package/services/server/SegmentServing.js +1146 -1146
- package/services/server/ViewerRequests.js +522 -522
- package/services/server/WaitLedger.js +142 -142
- package/services/server/controllers/PlaybackController.js +36 -36
- package/services/server/controllers/SubtitleController.js +109 -109
- package/services/server/http-responses.js +60 -60
- package/services/server/transcode-session-files.js +325 -325
- package/services/server/wire-outputs.js +983 -983
- package/services/storage/Diagnostics.js +202 -202
- package/services/storage/MachineBudget.js +235 -235
- package/services/storage/allowance.js +107 -107
- package/services/storage/core-dumps.js +106 -106
- package/services/storage/files/CompletedFiles.js +391 -391
- package/services/storage/files/piece-from-whole-file.js +118 -118
- package/services/storage/free.js +71 -71
- package/services/storage/keep.js +48 -48
- package/services/storage/machine-memory.js +56 -56
- package/services/storage/memory-report.js +575 -575
- package/services/storage/piece-store/piece-disk-store.js +516 -516
- package/services/storage/piece-store/piece-lru.js +495 -495
- package/services/storage/piece-store/shared-piece-store.js +1880 -1880
- package/services/storage/returns.js +103 -103
- package/services/storage/segment-store/SegmentStore.js +1196 -1196
- package/services/storage/wire.js +175 -175
- package/services/torrent/Contents.js +324 -324
- package/services/torrent/demand/DemandRegister.js +202 -202
- package/services/torrent/demand/Urgency.js +137 -137
- package/services/torrent/demand/Window.js +118 -118
- package/services/torrent/demand/index.js +8 -8
- package/services/torrent/demand/pieces.js +140 -140
- package/services/torrent/download/SwarmSelection.js +386 -386
- package/services/torrent/download/registry.js +102 -102
- package/services/torrent/download/withdraw-claim.js +80 -80
- package/services/torrent/files.js +455 -455
- package/services/torrent/naming.js +619 -619
- package/services/torrent/piece-store-of.js +29 -29
- package/services/torrent/supply-margin.js +262 -262
- package/services/torrent/torrent-pool.js +2675 -2635
- package/services/torrent/torrent-source-key.js +38 -38
- package/services/torrent/worker/background-fill.js +213 -213
- package/services/torrent/worker/channel.js +271 -271
- package/services/torrent/worker/client.js +765 -760
- package/services/torrent/worker/fastest-wires.js +342 -342
- package/services/torrent/worker/handle-state.js +23 -23
- package/services/torrent/worker/held-bytes.js +155 -155
- package/services/torrent/worker/install-webrtc-shim.js +32 -32
- package/services/torrent/worker/piece-reader.js +1078 -1078
- package/services/torrent/worker/pool-adapter.js +365 -350
- package/services/torrent/worker/protocol.js +184 -182
- package/services/torrent/worker/resume-warm.js +107 -107
- package/services/torrent/worker/webrtc-shim.js +219 -219
- package/services/torrent/worker/worker.js +1129 -1116
- package/services/transport/data-channel-handler.js +1356 -1356
- package/services/transport/delivery-probe.js +842 -842
- package/services/transport/health-collector.js +53 -53
- package/services/transport/nat-classifier.js +231 -231
- package/services/transport/packet-witness.js +808 -808
- package/services/transport/port-mapper.js +235 -235
- package/services/transport/registry-api.js +81 -81
- package/services/transport/tunnel-client.js +447 -447
- package/services/transport/usrsctp-state.js +175 -175
- package/services/transport/webrtc-manager.js +484 -484
- package/services/viewer/Assignments.js +340 -340
- package/services/viewer/PriorityMap.js +390 -390
- package/services/viewer/PriorityOrchestrator.js +325 -325
- package/services/viewer/Viewer.js +657 -657
- package/services/viewer/Viewers.js +368 -368
- package/services/viewer/active-output.js +37 -37
- package/services/viewer/audio-start.js +46 -46
- package/services/viewer/buffer-trend.js +81 -81
- package/services/viewer/choices.js +647 -647
- package/services/viewer/positions.js +126 -126
- package/services/viewer/report-intake.js +46 -46
- package/store/source-registry.js +73 -73
- package/test/asset-paths.test.js +28 -28
- package/test/audio-inventory.test.js +176 -176
- package/test/audio-track-bitrate.test.js +40 -40
- package/test/auto-quality-step.test.js +707 -707
- package/test/available-share.test.js +76 -76
- package/test/background-fill.test.js +190 -190
- package/test/client-log-file.test.js +110 -110
- package/test/completed-files.test.js +115 -115
- package/test/concurrent-cost.test.js +148 -148
- package/test/container-media-info.test.js +228 -228
- package/test/contention.test.js +76 -76
- package/test/core-dumps.test.js +57 -57
- package/test/coverage-follows-the-disk.test.js +228 -228
- package/test/coverage-map.test.js +195 -195
- package/test/cue-push-carries-what-the-walk-found.test.js +58 -58
- package/test/cut-times-timeline.test.js +65 -65
- package/test/cuts-follow-published-grid.test.js +109 -109
- package/test/data-channel-frame.test.js +63 -63
- package/test/dead-torrent-handle.test.js +52 -52
- package/test/declared-rates.test.js +116 -116
- package/test/declared-tracks.test.js +35 -35
- package/test/decode-cost-fit.test.js +202 -202
- package/test/decode-cost.test.js +462 -462
- package/test/delivery-probe.test.js +393 -393
- package/test/demand-register.test.js +206 -206
- package/test/dht-bootstrap.test.js +72 -72
- package/test/drain-threat.test.js +65 -65
- package/test/duplicate-add.test.js +48 -48
- package/test/encode-admission.test.js +146 -146
- package/test/encode-cost.test.js +283 -283
- package/test/encode-exit.test.js +111 -111
- package/test/encode-orchestrator.test.js +419 -419
- package/test/encode-plan-viewers.test.js +718 -718
- package/test/encode-plan.test.js +545 -545
- package/test/encode-run-state.test.js +314 -314
- package/test/encode-run.test.js +161 -161
- package/test/encode-slope.test.js +46 -46
- package/test/encoded-output.test.js +39 -39
- package/test/encoder-kinds.test.js +122 -122
- package/test/encoder-readings.test.js +46 -46
- package/test/every-claimant-is-told-its-share.test.js +152 -152
- package/test/failed-encoder-still-serves-what-exists.test.js +354 -354
- package/test/failed-start-breaker.test.js +202 -202
- package/test/fastest-wires.test.js +143 -143
- package/test/ffmpeg-stream-counts.test.js +78 -78
- package/test/file-edges.test.js +191 -191
- package/test/fmp4-self-contained.test.js +80 -80
- package/test/fmp4-timestamps.test.js +140 -140
- package/test/fmp4-track-completeness.test.js +41 -41
- package/test/format-keyed-outputs.test.js +78 -78
- package/test/health-metrics.test.js +38 -38
- package/test/held-request-width.test.js +157 -157
- package/test/helpers/encode-run.js +136 -136
- package/test/helpers/held-file.js +29 -29
- package/test/helpers/manager.js +57 -57
- package/test/helpers/output-spec.js +63 -63
- package/test/host-load.test.js +68 -68
- package/test/index-accuracy-is-about-the-file.test.js +128 -128
- package/test/init-compat.test.js +219 -219
- package/test/input-lost-quiets-the-plan.test.js +261 -261
- package/test/input-unavailable.test.js +48 -48
- package/test/installation-id.test.js +39 -39
- package/test/keeping-period.test.js +83 -83
- package/test/keyframe-index-accuracy.test.js +188 -188
- package/test/keyframe-table-budget.test.js +102 -102
- package/test/keyframes-belong-to-the-file.test.js +249 -249
- package/test/learned-median.test.js +37 -37
- package/test/letting-go-asks-who-is-watching.test.js +191 -191
- package/test/limit-row-by-area.test.js +60 -60
- package/test/link-budget.test.js +111 -111
- package/test/link-probe.test.js +56 -56
- package/test/local-encoding-data.test.js +273 -273
- package/test/logger-repeats.test.js +134 -134
- package/test/look-ahead-coverage.test.js +56 -56
- package/test/manager-surface.test.js +292 -292
- package/test/matroska-cues-track.test.js +192 -192
- package/test/memory-budget.test.js +390 -390
- package/test/move-cost.test.js +216 -216
- package/test/mp4-subtitles.test.js +173 -173
- package/test/one-authority.test.js +285 -285
- package/test/one-budget.test.js +149 -149
- package/test/one-output-one-session.test.js +204 -204
- package/test/one-resource-divided.test.js +185 -185
- package/test/one-serving-output-per-height.test.js +458 -458
- package/test/orchestrator-wired.test.js +206 -206
- package/test/output-catalog.test.js +39 -39
- package/test/output-shape.test.js +21 -21
- package/test/output-spec.test.js +251 -251
- package/test/output-speed.test.js +88 -88
- package/test/outputs-being-prepared.test.js +40 -40
- package/test/packet-witness-ring.test.js +236 -236
- package/test/packet-witness.test.js +148 -148
- package/test/piece-disk-store.test.js +381 -381
- package/test/piece-from-whole-file.test.js +129 -129
- package/test/piece-lru.test.js +319 -319
- package/test/piece-reader.test.js +162 -162
- package/test/piece-selection-offset.test.js +85 -85
- package/test/piece-store-eviction.test.js +560 -560
- package/test/piece-store-never-refuses.test.js +153 -153
- package/test/piece-store-reservations.test.js +318 -318
- package/test/piece-store-slow-disk.test.js +159 -159
- package/test/piece-tail.test.js +97 -97
- package/test/plan-host-timings.test.js +68 -68
- package/test/plan-is-steady.test.js +225 -225
- package/test/priority-map-download.test.js +174 -174
- package/test/priority-map-emptied.test.js +204 -204
- package/test/priority-map-per-output.test.js +215 -215
- package/test/priority-map.test.js +163 -163
- package/test/probe-wedge-certainty.test.js +60 -60
- package/test/produced-copy-choice.test.js +213 -213
- package/test/published-timeline.test.js +83 -83
- package/test/quality-offer.test.js +162 -162
- package/test/quality-variants.test.js +1229 -1229
- package/test/rate-control-in-the-output.test.js +180 -180
- package/test/read-survives-withdrawal.test.js +164 -164
- package/test/read-window.test.js +365 -365
- package/test/resume-warm.test.js +39 -39
- package/test/run-graph-drift.test.js +37 -37
- package/test/run-inputs.test.js +80 -80
- package/test/run-intervals.test.js +100 -100
- package/test/run-position-follows-published-grid.test.js +89 -89
- package/test/run-progress.test.js +65 -65
- package/test/same-height-switch.test.js +664 -664
- package/test/seek-frees-the-viewers-requests.test.js +217 -217
- package/test/seek-landing.test.js +109 -109
- package/test/seek-target-not-superseded.test.js +195 -195
- package/test/segment-closed-on-both-branches.test.js +96 -96
- package/test/segment-cut-times.test.js +63 -63
- package/test/segment-demand.test.js +80 -80
- package/test/segment-hold-supersede.test.js +91 -91
- package/test/segment-judged-by-its-own-header.test.js +221 -221
- package/test/segment-serve-wiring.test.js +475 -475
- package/test/segment-store-eviction.test.js +247 -247
- package/test/segment-store.test.js +323 -323
- package/test/segment-true-start.test.js +86 -86
- package/test/segments-are-shared.test.js +176 -176
- package/test/send-chunk-bytes.test.js +86 -86
- package/test/serving-existing-output.test.js +102 -102
- package/test/serving-output.test.js +102 -102
- package/test/shared-piece-store.test.js +310 -310
- package/test/sidecar-naming.test.js +142 -142
- package/test/source-file.test.js +133 -133
- package/test/source-files-route.test.js +103 -103
- package/test/split-a-stretch.test.js +139 -139
- package/test/stale-request-after-seek.test.js +158 -158
- package/test/startup-readings.test.js +124 -124
- package/test/stream-route.test.js +182 -182
- package/test/subtitle-cue-framing.test.js +200 -200
- package/test/subtitle-cue-source.test.js +102 -102
- package/test/subtitle-cue-walk.test.js +370 -370
- package/test/subtitle-cursor.test.js +63 -63
- package/test/subtitle-defaults.test.js +118 -118
- package/test/subtitle-language.test.js +252 -252
- package/test/subtitle-track-numbering.test.js +386 -386
- package/test/supply-margin.test.js +177 -177
- package/test/swarm-follows-readers.test.js +174 -174
- package/test/swarm-reach.test.js +130 -130
- package/test/swarm-selection.test.js +220 -220
- package/test/tail-duplication.test.js +167 -167
- package/test/the-channel-has-both-ends.test.js +91 -91
- package/test/the-torrent-thread-serves-bytes.test.js +107 -107
- package/test/thread-pool.test.js +53 -53
- package/test/timeline.test.js +84 -84
- package/test/torrent-contents.test.js +235 -235
- package/test/torrent-cost.test.js +71 -71
- package/test/torrent-files.test.js +221 -221
- package/test/torrent-naming.test.js +255 -255
- package/test/tracks-begin-together.test.js +209 -209
- package/test/tunnel-renewal.test.js +167 -167
- package/test/two-viewers-one-picture.test.js +407 -407
- package/test/upload-hurry.test.js +186 -186
- package/test/usrsctp-state.test.js +131 -131
- package/test/video-facts.test.js +102 -102
- package/test/viewer-assignments.test.js +143 -143
- package/test/viewer-outputs.test.js +285 -285
- package/test/viewer-presence.test.js +116 -116
- package/test/viewer-requests-admission.test.js +89 -89
- package/test/viewer-states.test.js +189 -189
- package/test/viewer-subtitle-subscription.test.js +96 -96
- package/test/viewer.test.js +139 -139
- package/test/viewing-generations.test.js +390 -390
- package/test/visible-rung.test.js +37 -37
- package/test/wait-ledger.test.js +100 -100
- package/test/webrtc-shim.test.js +149 -149
- package/test/wedge-certainty.test.js +131 -131
- package/test/withdraw-piece-claim.test.js +212 -212
- package/test/worker-channel.test.js +158 -158
- package/utils/client-log-file.js +151 -151
- package/utils/logger.js +312 -312
- package/utils/parse-range.js +25 -25
- package/utils/perf.js +59 -59
|
@@ -1,174 +1,174 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file What the download is told when the priority map changes.
|
|
3
|
-
*
|
|
4
|
-
* The map states seconds of film against a number. This is the one place those
|
|
5
|
-
* seconds become bytes, and the one place the map's own scale — as many bands
|
|
6
|
-
* as the film needs — is fitted onto the five levels the register has. Both
|
|
7
|
-
* were written on 2026-09-05 to replace the windows the reads themselves used
|
|
8
|
-
* to declare: fifteen reads on one file were fifteen windows on a piece store
|
|
9
|
-
* holding sixteen pieces, and half of all evictions then took a piece a reader
|
|
10
|
-
* had said it wanted.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import test from "node:test";
|
|
14
|
-
import assert from "node:assert/strict";
|
|
15
|
-
import { TorrentPool } from "../services/torrent/torrent-pool.js";
|
|
16
|
-
import { demandFor, forgetTorrent } from "../services/torrent/download/registry.js";
|
|
17
|
-
import { Urgency } from "../services/torrent/demand/index.js";
|
|
18
|
-
import { mapForViewer, runsOf } from "../services/viewer/PriorityMap.js";
|
|
19
|
-
|
|
20
|
-
/** A torrent that is nothing but one file of a known length. */
|
|
21
|
-
function torrentOf({ length = 1_000_000, offset = 0 } = {}) {
|
|
22
|
-
return {
|
|
23
|
-
infoHash: `hash-${Math.random().toString(36).slice(2)}`,
|
|
24
|
-
pieceLength: 1024,
|
|
25
|
-
files: [{ offset, length, name: "film.mkv" }],
|
|
26
|
-
_selections: { _items: [] },
|
|
27
|
-
_select() {},
|
|
28
|
-
_deselect() {},
|
|
29
|
-
critical() {}
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** The pool's method under test, called without building a pool. */
|
|
34
|
-
const applyPriorityMap = TorrentPool.prototype.applyPriorityMap;
|
|
35
|
-
|
|
36
|
-
test("seconds of film become bytes of the file", () => {
|
|
37
|
-
const torrent = torrentOf({ length: 1000 });
|
|
38
|
-
try {
|
|
39
|
-
applyPriorityMap.call(null, torrent, 0, [
|
|
40
|
-
{ from: 0, to: 100, priority: 5 },
|
|
41
|
-
{ from: 100, to: 200, priority: 3 }
|
|
42
|
-
], 200);
|
|
43
|
-
|
|
44
|
-
const { register } = demandFor(torrent);
|
|
45
|
-
const stated = register.windows().filter((one) => String(one.claimant).startsWith("priority-map:"));
|
|
46
|
-
assert.equal(stated.length, 2);
|
|
47
|
-
const first = stated.find((one) => one.byteStart === 0);
|
|
48
|
-
assert.equal(first.byteEnd, 499, "half the film is half the file");
|
|
49
|
-
const second = stated.find((one) => one.byteStart === 500);
|
|
50
|
-
assert.equal(second.byteEnd, 999, "the last byte of the file, not one past it");
|
|
51
|
-
} finally {
|
|
52
|
-
forgetTorrent(torrent);
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
test("a zone is stated at the level it means, not at the level of its position", () => {
|
|
57
|
-
const torrent = torrentOf();
|
|
58
|
-
try {
|
|
59
|
-
// A viewer 300 s into a 3000 s film: one band behind them that nobody is
|
|
60
|
-
// approaching, then bands of decreasing urgency ahead.
|
|
61
|
-
const zones = runsOf(mapForViewer({
|
|
62
|
-
atSeconds: 300,
|
|
63
|
-
durationSeconds: 3000,
|
|
64
|
-
allowanceSeconds: 10
|
|
65
|
-
}));
|
|
66
|
-
assert.ok(zones.length > 5, "the map should be finer than the register's levels");
|
|
67
|
-
|
|
68
|
-
applyPriorityMap.call(null, torrent, 0, zones, 3000);
|
|
69
|
-
const { register } = demandFor(torrent);
|
|
70
|
-
const stated = register.windows().filter((one) => String(one.claimant).startsWith("priority-map:"));
|
|
71
|
-
|
|
72
|
-
const near = stated.filter((one) => one.urgency === Urgency.NEAR);
|
|
73
|
-
assert.equal(near.length, 1, "exactly one band is where the viewer is");
|
|
74
|
-
assert.equal(near[0].byteStart, Math.floor((300 / 3000) * 1_000_000));
|
|
75
|
-
|
|
76
|
-
const behind = stated.filter((one) => one.urgency === Urgency.BEHIND);
|
|
77
|
-
assert.equal(behind.length, 1, "what nobody is approaching is the one thing wanted last");
|
|
78
|
-
assert.equal(behind[0].byteStart, 0, "and it is the stretch behind the viewer");
|
|
79
|
-
|
|
80
|
-
assert.ok(
|
|
81
|
-
stated.some((one) => one.urgency === Urgency.AHEAD),
|
|
82
|
-
"the lead being built was stated at no level of its own"
|
|
83
|
-
);
|
|
84
|
-
assert.ok(
|
|
85
|
-
stated.every((one) => one.urgency !== Urgency.BLOCKED),
|
|
86
|
-
"the map claimed BLOCKED, which only a reader stopped on those bytes may say"
|
|
87
|
-
);
|
|
88
|
-
} finally {
|
|
89
|
-
forgetTorrent(torrent);
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
test("a viewer who has stopped the picture makes the whole film wanted last", () => {
|
|
94
|
-
const torrent = torrentOf();
|
|
95
|
-
try {
|
|
96
|
-
const zones = runsOf(mapForViewer({
|
|
97
|
-
atSeconds: 300,
|
|
98
|
-
durationSeconds: 3000,
|
|
99
|
-
allowanceSeconds: 10,
|
|
100
|
-
playing: false
|
|
101
|
-
}));
|
|
102
|
-
applyPriorityMap.call(null, torrent, 0, zones, 3000);
|
|
103
|
-
|
|
104
|
-
const { register } = demandFor(torrent);
|
|
105
|
-
const stated = register.windows().filter((one) => String(one.claimant).startsWith("priority-map:"));
|
|
106
|
-
// A PAUSE REMOVES THE TIME, NOT THE DIRECTION. What they have watched is
|
|
107
|
-
// behind them and what they have not is in front, exactly as while they were
|
|
108
|
-
// watching; what changes is that nothing has a time any more, so every band
|
|
109
|
-
// of theirs yields to anybody who is on their way somewhere.
|
|
110
|
-
assert.ok(stated.length >= 2, "their position survives the pause");
|
|
111
|
-
for (const one of stated) {
|
|
112
|
-
assert.ok(
|
|
113
|
-
one.urgency === Urgency.BEHIND || one.urgency === Urgency.TAIL,
|
|
114
|
-
"and nothing of theirs is urgent, because nobody is coming"
|
|
115
|
-
);
|
|
116
|
-
}
|
|
117
|
-
const front = stated.filter((one) => one.urgency === Urgency.TAIL);
|
|
118
|
-
assert.ok(front.length >= 1, "the film in front of them is still in front");
|
|
119
|
-
assert.ok(
|
|
120
|
-
front[0].byteStart >= Math.floor((300 / 3000) * 1_000_000),
|
|
121
|
-
"and it begins where they stopped"
|
|
122
|
-
);
|
|
123
|
-
// Wanted last ABSOLUTELY, not relative to this film's own map. The two
|
|
124
|
-
// speculative levels are withheld across every torrent at once while
|
|
125
|
-
// anything urgent is missing anywhere, so this has to lose to a film
|
|
126
|
-
// somebody is actually watching — and judged against itself alone it would
|
|
127
|
-
// be the most urgent thing there is.
|
|
128
|
-
const behind = stated.filter((one) => one.urgency === Urgency.BEHIND);
|
|
129
|
-
assert.equal(behind.length, 1, "what they have already watched is wanted last of all");
|
|
130
|
-
assert.equal(behind[0].byteStart, 0, "and it is the stretch from the beginning to where they stopped");
|
|
131
|
-
} finally {
|
|
132
|
-
forgetTorrent(torrent);
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
test("a map with fewer bands than the last one leaves nothing stated behind", () => {
|
|
137
|
-
const torrent = torrentOf();
|
|
138
|
-
try {
|
|
139
|
-
applyPriorityMap.call(null, torrent, 0, [
|
|
140
|
-
{ from: 0, to: 100, priority: 5 },
|
|
141
|
-
{ from: 100, to: 200, priority: 4 },
|
|
142
|
-
{ from: 200, to: 300, priority: 3 }
|
|
143
|
-
], 300);
|
|
144
|
-
const { register } = demandFor(torrent);
|
|
145
|
-
assert.equal(
|
|
146
|
-
register.windows().filter((one) => String(one.claimant).startsWith("priority-map:")).length,
|
|
147
|
-
3
|
|
148
|
-
);
|
|
149
|
-
|
|
150
|
-
applyPriorityMap.call(null, torrent, 0, [{ from: 0, to: 300, priority: 5 }], 300);
|
|
151
|
-
assert.equal(
|
|
152
|
-
register.windows().filter((one) => String(one.claimant).startsWith("priority-map:")).length,
|
|
153
|
-
1,
|
|
154
|
-
"bands the map no longer has went on being asked for"
|
|
155
|
-
);
|
|
156
|
-
} finally {
|
|
157
|
-
forgetTorrent(torrent);
|
|
158
|
-
}
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
test("a file of unknown length or duration is not guessed at", () => {
|
|
162
|
-
const torrent = torrentOf();
|
|
163
|
-
try {
|
|
164
|
-
applyPriorityMap.call(null, torrent, 0, [{ from: 0, to: 100, priority: 5 }], 0);
|
|
165
|
-
applyPriorityMap.call(null, torrent, 7, [{ from: 0, to: 100, priority: 5 }], 300);
|
|
166
|
-
const { register } = demandFor(torrent);
|
|
167
|
-
assert.equal(
|
|
168
|
-
register.windows().filter((one) => String(one.claimant).startsWith("priority-map:")).length,
|
|
169
|
-
0
|
|
170
|
-
);
|
|
171
|
-
} finally {
|
|
172
|
-
forgetTorrent(torrent);
|
|
173
|
-
}
|
|
174
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* @file What the download is told when the priority map changes.
|
|
3
|
+
*
|
|
4
|
+
* The map states seconds of film against a number. This is the one place those
|
|
5
|
+
* seconds become bytes, and the one place the map's own scale — as many bands
|
|
6
|
+
* as the film needs — is fitted onto the five levels the register has. Both
|
|
7
|
+
* were written on 2026-09-05 to replace the windows the reads themselves used
|
|
8
|
+
* to declare: fifteen reads on one file were fifteen windows on a piece store
|
|
9
|
+
* holding sixteen pieces, and half of all evictions then took a piece a reader
|
|
10
|
+
* had said it wanted.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import test from "node:test";
|
|
14
|
+
import assert from "node:assert/strict";
|
|
15
|
+
import { TorrentPool } from "../services/torrent/torrent-pool.js";
|
|
16
|
+
import { demandFor, forgetTorrent } from "../services/torrent/download/registry.js";
|
|
17
|
+
import { Urgency } from "../services/torrent/demand/index.js";
|
|
18
|
+
import { mapForViewer, runsOf } from "../services/viewer/PriorityMap.js";
|
|
19
|
+
|
|
20
|
+
/** A torrent that is nothing but one file of a known length. */
|
|
21
|
+
function torrentOf({ length = 1_000_000, offset = 0 } = {}) {
|
|
22
|
+
return {
|
|
23
|
+
infoHash: `hash-${Math.random().toString(36).slice(2)}`,
|
|
24
|
+
pieceLength: 1024,
|
|
25
|
+
files: [{ offset, length, name: "film.mkv" }],
|
|
26
|
+
_selections: { _items: [] },
|
|
27
|
+
_select() {},
|
|
28
|
+
_deselect() {},
|
|
29
|
+
critical() {}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** The pool's method under test, called without building a pool. */
|
|
34
|
+
const applyPriorityMap = TorrentPool.prototype.applyPriorityMap;
|
|
35
|
+
|
|
36
|
+
test("seconds of film become bytes of the file", () => {
|
|
37
|
+
const torrent = torrentOf({ length: 1000 });
|
|
38
|
+
try {
|
|
39
|
+
applyPriorityMap.call(null, torrent, 0, [
|
|
40
|
+
{ from: 0, to: 100, priority: 5 },
|
|
41
|
+
{ from: 100, to: 200, priority: 3 }
|
|
42
|
+
], 200);
|
|
43
|
+
|
|
44
|
+
const { register } = demandFor(torrent);
|
|
45
|
+
const stated = register.windows().filter((one) => String(one.claimant).startsWith("priority-map:"));
|
|
46
|
+
assert.equal(stated.length, 2);
|
|
47
|
+
const first = stated.find((one) => one.byteStart === 0);
|
|
48
|
+
assert.equal(first.byteEnd, 499, "half the film is half the file");
|
|
49
|
+
const second = stated.find((one) => one.byteStart === 500);
|
|
50
|
+
assert.equal(second.byteEnd, 999, "the last byte of the file, not one past it");
|
|
51
|
+
} finally {
|
|
52
|
+
forgetTorrent(torrent);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("a zone is stated at the level it means, not at the level of its position", () => {
|
|
57
|
+
const torrent = torrentOf();
|
|
58
|
+
try {
|
|
59
|
+
// A viewer 300 s into a 3000 s film: one band behind them that nobody is
|
|
60
|
+
// approaching, then bands of decreasing urgency ahead.
|
|
61
|
+
const zones = runsOf(mapForViewer({
|
|
62
|
+
atSeconds: 300,
|
|
63
|
+
durationSeconds: 3000,
|
|
64
|
+
allowanceSeconds: 10
|
|
65
|
+
}));
|
|
66
|
+
assert.ok(zones.length > 5, "the map should be finer than the register's levels");
|
|
67
|
+
|
|
68
|
+
applyPriorityMap.call(null, torrent, 0, zones, 3000);
|
|
69
|
+
const { register } = demandFor(torrent);
|
|
70
|
+
const stated = register.windows().filter((one) => String(one.claimant).startsWith("priority-map:"));
|
|
71
|
+
|
|
72
|
+
const near = stated.filter((one) => one.urgency === Urgency.NEAR);
|
|
73
|
+
assert.equal(near.length, 1, "exactly one band is where the viewer is");
|
|
74
|
+
assert.equal(near[0].byteStart, Math.floor((300 / 3000) * 1_000_000));
|
|
75
|
+
|
|
76
|
+
const behind = stated.filter((one) => one.urgency === Urgency.BEHIND);
|
|
77
|
+
assert.equal(behind.length, 1, "what nobody is approaching is the one thing wanted last");
|
|
78
|
+
assert.equal(behind[0].byteStart, 0, "and it is the stretch behind the viewer");
|
|
79
|
+
|
|
80
|
+
assert.ok(
|
|
81
|
+
stated.some((one) => one.urgency === Urgency.AHEAD),
|
|
82
|
+
"the lead being built was stated at no level of its own"
|
|
83
|
+
);
|
|
84
|
+
assert.ok(
|
|
85
|
+
stated.every((one) => one.urgency !== Urgency.BLOCKED),
|
|
86
|
+
"the map claimed BLOCKED, which only a reader stopped on those bytes may say"
|
|
87
|
+
);
|
|
88
|
+
} finally {
|
|
89
|
+
forgetTorrent(torrent);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("a viewer who has stopped the picture makes the whole film wanted last", () => {
|
|
94
|
+
const torrent = torrentOf();
|
|
95
|
+
try {
|
|
96
|
+
const zones = runsOf(mapForViewer({
|
|
97
|
+
atSeconds: 300,
|
|
98
|
+
durationSeconds: 3000,
|
|
99
|
+
allowanceSeconds: 10,
|
|
100
|
+
playing: false
|
|
101
|
+
}));
|
|
102
|
+
applyPriorityMap.call(null, torrent, 0, zones, 3000);
|
|
103
|
+
|
|
104
|
+
const { register } = demandFor(torrent);
|
|
105
|
+
const stated = register.windows().filter((one) => String(one.claimant).startsWith("priority-map:"));
|
|
106
|
+
// A PAUSE REMOVES THE TIME, NOT THE DIRECTION. What they have watched is
|
|
107
|
+
// behind them and what they have not is in front, exactly as while they were
|
|
108
|
+
// watching; what changes is that nothing has a time any more, so every band
|
|
109
|
+
// of theirs yields to anybody who is on their way somewhere.
|
|
110
|
+
assert.ok(stated.length >= 2, "their position survives the pause");
|
|
111
|
+
for (const one of stated) {
|
|
112
|
+
assert.ok(
|
|
113
|
+
one.urgency === Urgency.BEHIND || one.urgency === Urgency.TAIL,
|
|
114
|
+
"and nothing of theirs is urgent, because nobody is coming"
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
const front = stated.filter((one) => one.urgency === Urgency.TAIL);
|
|
118
|
+
assert.ok(front.length >= 1, "the film in front of them is still in front");
|
|
119
|
+
assert.ok(
|
|
120
|
+
front[0].byteStart >= Math.floor((300 / 3000) * 1_000_000),
|
|
121
|
+
"and it begins where they stopped"
|
|
122
|
+
);
|
|
123
|
+
// Wanted last ABSOLUTELY, not relative to this film's own map. The two
|
|
124
|
+
// speculative levels are withheld across every torrent at once while
|
|
125
|
+
// anything urgent is missing anywhere, so this has to lose to a film
|
|
126
|
+
// somebody is actually watching — and judged against itself alone it would
|
|
127
|
+
// be the most urgent thing there is.
|
|
128
|
+
const behind = stated.filter((one) => one.urgency === Urgency.BEHIND);
|
|
129
|
+
assert.equal(behind.length, 1, "what they have already watched is wanted last of all");
|
|
130
|
+
assert.equal(behind[0].byteStart, 0, "and it is the stretch from the beginning to where they stopped");
|
|
131
|
+
} finally {
|
|
132
|
+
forgetTorrent(torrent);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test("a map with fewer bands than the last one leaves nothing stated behind", () => {
|
|
137
|
+
const torrent = torrentOf();
|
|
138
|
+
try {
|
|
139
|
+
applyPriorityMap.call(null, torrent, 0, [
|
|
140
|
+
{ from: 0, to: 100, priority: 5 },
|
|
141
|
+
{ from: 100, to: 200, priority: 4 },
|
|
142
|
+
{ from: 200, to: 300, priority: 3 }
|
|
143
|
+
], 300);
|
|
144
|
+
const { register } = demandFor(torrent);
|
|
145
|
+
assert.equal(
|
|
146
|
+
register.windows().filter((one) => String(one.claimant).startsWith("priority-map:")).length,
|
|
147
|
+
3
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
applyPriorityMap.call(null, torrent, 0, [{ from: 0, to: 300, priority: 5 }], 300);
|
|
151
|
+
assert.equal(
|
|
152
|
+
register.windows().filter((one) => String(one.claimant).startsWith("priority-map:")).length,
|
|
153
|
+
1,
|
|
154
|
+
"bands the map no longer has went on being asked for"
|
|
155
|
+
);
|
|
156
|
+
} finally {
|
|
157
|
+
forgetTorrent(torrent);
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("a file of unknown length or duration is not guessed at", () => {
|
|
162
|
+
const torrent = torrentOf();
|
|
163
|
+
try {
|
|
164
|
+
applyPriorityMap.call(null, torrent, 0, [{ from: 0, to: 100, priority: 5 }], 0);
|
|
165
|
+
applyPriorityMap.call(null, torrent, 7, [{ from: 0, to: 100, priority: 5 }], 300);
|
|
166
|
+
const { register } = demandFor(torrent);
|
|
167
|
+
assert.equal(
|
|
168
|
+
register.windows().filter((one) => String(one.claimant).startsWith("priority-map:")).length,
|
|
169
|
+
0
|
|
170
|
+
);
|
|
171
|
+
} finally {
|
|
172
|
+
forgetTorrent(torrent);
|
|
173
|
+
}
|
|
174
|
+
});
|