@torrent-tv/proxy 2.87.5 → 2.87.7
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 +8 -0
- 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 +101 -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 +1 -1
- 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/audio-tracks/post.js +29 -0
- 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/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 +4 -0
- 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 +245 -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 +131 -59
- 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 +40 -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 +2 -2
- package/services/torrent/worker/background-fill.js +213 -213
- package/services/torrent/worker/channel.js +271 -271
- 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 +1 -1
- package/services/torrent/worker/resume-warm.js +107 -107
- package/services/torrent/worker/webrtc-shim.js +219 -219
- 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 +71 -71
- package/test/asset-paths.test.js +28 -28
- package/test/audio-inventory.test.js +212 -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/source-registry.test.js +18 -18
- 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
- package/{services/torrent → utils}/torrent-source-key.js +38 -38
|
@@ -1,116 +1,116 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file What the master playlist declares a variant carries.
|
|
3
|
-
*
|
|
4
|
-
* Measured, and the measurement was available all along: it used to be
|
|
5
|
-
* `height * height * 3.2`, justified by a comment saying no measurement existed
|
|
6
|
-
* before encoding starts. Field 2026-09-08: 3.73 Mbit/s declared for a file
|
|
7
|
-
* carrying 18.4, and the browser sizes its cushion in BYTES from that figure —
|
|
8
|
-
* 120 s asked bought 26 s of film, and the deepest it ever held was 17.1 s.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import test from "node:test";
|
|
12
|
-
import assert from "node:assert/strict";
|
|
13
|
-
import { declaredRates } from "../services/encode/output/rates.js";
|
|
14
|
-
import { bitrateFor } from "../services/encode/output/playlists.js";
|
|
15
|
-
|
|
16
|
-
// The field file: 2 806 246 976 bytes over 20:18, cut into 291 pieces.
|
|
17
|
-
const FIELD = { fileLength: 2_806_246_976, durationSeconds: 1218.73 };
|
|
18
|
-
|
|
19
|
-
test("the average is the file's own length over its duration", () => {
|
|
20
|
-
const { averageBitsPerSecond } = declaredRates(FIELD);
|
|
21
|
-
|
|
22
|
-
// 18.4 Mbit/s, against the 3.73 that was declared.
|
|
23
|
-
assert.ok(
|
|
24
|
-
Math.abs(averageBitsPerSecond / 1_000_000 - 18.42) < 0.01,
|
|
25
|
-
`got ${averageBitsPerSecond}`
|
|
26
|
-
);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
test("nothing measured is stated as nothing, not as a guess", () => {
|
|
30
|
-
assert.deepEqual(
|
|
31
|
-
declaredRates({ fileLength: 0, durationSeconds: 0 }),
|
|
32
|
-
{ averageBitsPerSecond: 0, peakOverAverage: 1 }
|
|
33
|
-
);
|
|
34
|
-
assert.deepEqual(
|
|
35
|
-
declaredRates({ fileLength: 100, durationSeconds: 0 }),
|
|
36
|
-
{ averageBitsPerSecond: 0, peakOverAverage: 1 }
|
|
37
|
-
);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test("the peak comes from the biggest piece made and the span it covers", () => {
|
|
41
|
-
// The field peak: 38.4 MB over 4.2 s is 73 Mbit/s, four times the average.
|
|
42
|
-
const boundaries = Array.from({ length: 292 }, (_, index) => index * 4.2);
|
|
43
|
-
const { averageBitsPerSecond, peakOverAverage } = declaredRates({
|
|
44
|
-
...FIELD,
|
|
45
|
-
largest: { index: 2, size: 40_288_299 },
|
|
46
|
-
boundaries
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
const peak = averageBitsPerSecond * peakOverAverage;
|
|
50
|
-
assert.ok(Math.abs(peak / 1_000_000 - 76.7) < 0.5, `got ${peak / 1_000_000}Mbit/s`);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
test("no piece made yet means the peak is not yet measured, so it equals the average", () => {
|
|
54
|
-
// A ratio invented meanwhile is exactly the fabrication this replaced.
|
|
55
|
-
assert.equal(declaredRates(FIELD).peakOverAverage, 1);
|
|
56
|
-
assert.equal(
|
|
57
|
-
declaredRates({ ...FIELD, largest: { index: -1, size: 0 }, boundaries: [0, 4] }).peakOverAverage,
|
|
58
|
-
1
|
|
59
|
-
);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
test("the peak is never below the average", () => {
|
|
63
|
-
// A short session whose only piece happens to be a small one must not lower
|
|
64
|
-
// the figure the player sizes its cushion from.
|
|
65
|
-
const boundaries = Array.from({ length: 292 }, (_, index) => index * 4.2);
|
|
66
|
-
assert.equal(
|
|
67
|
-
declaredRates({ ...FIELD, largest: { index: 5, size: 1000 }, boundaries }).peakOverAverage,
|
|
68
|
-
1
|
|
69
|
-
);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
test("a smaller picture is declared at the pixel share of the source's rate", () => {
|
|
73
|
-
const measured = 18_420_000;
|
|
74
|
-
|
|
75
|
-
assert.equal(
|
|
76
|
-
bitrateFor({ averageBitsPerSecond: measured, height: 1080, sourceHeight: 1080 }),
|
|
77
|
-
Math.round(measured),
|
|
78
|
-
"the source's own height carries the source's bits"
|
|
79
|
-
);
|
|
80
|
-
assert.equal(
|
|
81
|
-
bitrateFor({ averageBitsPerSecond: measured, height: 2160, sourceHeight: 1080 }),
|
|
82
|
-
Math.round(measured),
|
|
83
|
-
"and a height above it carries no more — there is nothing to upscale from"
|
|
84
|
-
);
|
|
85
|
-
assert.equal(
|
|
86
|
-
bitrateFor({ averageBitsPerSecond: measured, height: 540, sourceHeight: 1080 }),
|
|
87
|
-
Math.round(measured / 4),
|
|
88
|
-
"half the height is a quarter of the pixels"
|
|
89
|
-
);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
test("a capped height is declared at its cap, which is exact", () => {
|
|
93
|
-
assert.equal(
|
|
94
|
-
bitrateFor({ averageBitsPerSecond: 18_420_000, height: 720, sourceHeight: 1080, capKbps: 2500 }),
|
|
95
|
-
2_500_000,
|
|
96
|
-
"what we impose is known, not estimated"
|
|
97
|
-
);
|
|
98
|
-
assert.equal(
|
|
99
|
-
bitrateFor({ averageBitsPerSecond: 1_000_000, height: 1080, sourceHeight: 1080, capKbps: 99_000 }),
|
|
100
|
-
1_000_000,
|
|
101
|
-
"and a cap above what the source carries takes nothing away from it"
|
|
102
|
-
);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
test("the floor is a floor on what may be declared, not a belief about content", () => {
|
|
106
|
-
assert.equal(
|
|
107
|
-
bitrateFor({ averageBitsPerSecond: 0, height: 1080, sourceHeight: 1080 }),
|
|
108
|
-
400_000,
|
|
109
|
-
"a file whose rate is not known yet"
|
|
110
|
-
);
|
|
111
|
-
assert.equal(
|
|
112
|
-
bitrateFor({ averageBitsPerSecond: 18_420_000, height: 4, sourceHeight: 1080 }),
|
|
113
|
-
400_000,
|
|
114
|
-
"and a variant so small the arithmetic would go under it"
|
|
115
|
-
);
|
|
116
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* @file What the master playlist declares a variant carries.
|
|
3
|
+
*
|
|
4
|
+
* Measured, and the measurement was available all along: it used to be
|
|
5
|
+
* `height * height * 3.2`, justified by a comment saying no measurement existed
|
|
6
|
+
* before encoding starts. Field 2026-09-08: 3.73 Mbit/s declared for a file
|
|
7
|
+
* carrying 18.4, and the browser sizes its cushion in BYTES from that figure —
|
|
8
|
+
* 120 s asked bought 26 s of film, and the deepest it ever held was 17.1 s.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import test from "node:test";
|
|
12
|
+
import assert from "node:assert/strict";
|
|
13
|
+
import { declaredRates } from "../services/encode/output/rates.js";
|
|
14
|
+
import { bitrateFor } from "../services/encode/output/playlists.js";
|
|
15
|
+
|
|
16
|
+
// The field file: 2 806 246 976 bytes over 20:18, cut into 291 pieces.
|
|
17
|
+
const FIELD = { fileLength: 2_806_246_976, durationSeconds: 1218.73 };
|
|
18
|
+
|
|
19
|
+
test("the average is the file's own length over its duration", () => {
|
|
20
|
+
const { averageBitsPerSecond } = declaredRates(FIELD);
|
|
21
|
+
|
|
22
|
+
// 18.4 Mbit/s, against the 3.73 that was declared.
|
|
23
|
+
assert.ok(
|
|
24
|
+
Math.abs(averageBitsPerSecond / 1_000_000 - 18.42) < 0.01,
|
|
25
|
+
`got ${averageBitsPerSecond}`
|
|
26
|
+
);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("nothing measured is stated as nothing, not as a guess", () => {
|
|
30
|
+
assert.deepEqual(
|
|
31
|
+
declaredRates({ fileLength: 0, durationSeconds: 0 }),
|
|
32
|
+
{ averageBitsPerSecond: 0, peakOverAverage: 1 }
|
|
33
|
+
);
|
|
34
|
+
assert.deepEqual(
|
|
35
|
+
declaredRates({ fileLength: 100, durationSeconds: 0 }),
|
|
36
|
+
{ averageBitsPerSecond: 0, peakOverAverage: 1 }
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("the peak comes from the biggest piece made and the span it covers", () => {
|
|
41
|
+
// The field peak: 38.4 MB over 4.2 s is 73 Mbit/s, four times the average.
|
|
42
|
+
const boundaries = Array.from({ length: 292 }, (_, index) => index * 4.2);
|
|
43
|
+
const { averageBitsPerSecond, peakOverAverage } = declaredRates({
|
|
44
|
+
...FIELD,
|
|
45
|
+
largest: { index: 2, size: 40_288_299 },
|
|
46
|
+
boundaries
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const peak = averageBitsPerSecond * peakOverAverage;
|
|
50
|
+
assert.ok(Math.abs(peak / 1_000_000 - 76.7) < 0.5, `got ${peak / 1_000_000}Mbit/s`);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("no piece made yet means the peak is not yet measured, so it equals the average", () => {
|
|
54
|
+
// A ratio invented meanwhile is exactly the fabrication this replaced.
|
|
55
|
+
assert.equal(declaredRates(FIELD).peakOverAverage, 1);
|
|
56
|
+
assert.equal(
|
|
57
|
+
declaredRates({ ...FIELD, largest: { index: -1, size: 0 }, boundaries: [0, 4] }).peakOverAverage,
|
|
58
|
+
1
|
|
59
|
+
);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("the peak is never below the average", () => {
|
|
63
|
+
// A short session whose only piece happens to be a small one must not lower
|
|
64
|
+
// the figure the player sizes its cushion from.
|
|
65
|
+
const boundaries = Array.from({ length: 292 }, (_, index) => index * 4.2);
|
|
66
|
+
assert.equal(
|
|
67
|
+
declaredRates({ ...FIELD, largest: { index: 5, size: 1000 }, boundaries }).peakOverAverage,
|
|
68
|
+
1
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("a smaller picture is declared at the pixel share of the source's rate", () => {
|
|
73
|
+
const measured = 18_420_000;
|
|
74
|
+
|
|
75
|
+
assert.equal(
|
|
76
|
+
bitrateFor({ averageBitsPerSecond: measured, height: 1080, sourceHeight: 1080 }),
|
|
77
|
+
Math.round(measured),
|
|
78
|
+
"the source's own height carries the source's bits"
|
|
79
|
+
);
|
|
80
|
+
assert.equal(
|
|
81
|
+
bitrateFor({ averageBitsPerSecond: measured, height: 2160, sourceHeight: 1080 }),
|
|
82
|
+
Math.round(measured),
|
|
83
|
+
"and a height above it carries no more — there is nothing to upscale from"
|
|
84
|
+
);
|
|
85
|
+
assert.equal(
|
|
86
|
+
bitrateFor({ averageBitsPerSecond: measured, height: 540, sourceHeight: 1080 }),
|
|
87
|
+
Math.round(measured / 4),
|
|
88
|
+
"half the height is a quarter of the pixels"
|
|
89
|
+
);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test("a capped height is declared at its cap, which is exact", () => {
|
|
93
|
+
assert.equal(
|
|
94
|
+
bitrateFor({ averageBitsPerSecond: 18_420_000, height: 720, sourceHeight: 1080, capKbps: 2500 }),
|
|
95
|
+
2_500_000,
|
|
96
|
+
"what we impose is known, not estimated"
|
|
97
|
+
);
|
|
98
|
+
assert.equal(
|
|
99
|
+
bitrateFor({ averageBitsPerSecond: 1_000_000, height: 1080, sourceHeight: 1080, capKbps: 99_000 }),
|
|
100
|
+
1_000_000,
|
|
101
|
+
"and a cap above what the source carries takes nothing away from it"
|
|
102
|
+
);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("the floor is a floor on what may be declared, not a belief about content", () => {
|
|
106
|
+
assert.equal(
|
|
107
|
+
bitrateFor({ averageBitsPerSecond: 0, height: 1080, sourceHeight: 1080 }),
|
|
108
|
+
400_000,
|
|
109
|
+
"a file whose rate is not known yet"
|
|
110
|
+
);
|
|
111
|
+
assert.equal(
|
|
112
|
+
bitrateFor({ averageBitsPerSecond: 18_420_000, height: 4, sourceHeight: 1080 }),
|
|
113
|
+
400_000,
|
|
114
|
+
"and a variant so small the arithmetic would go under it"
|
|
115
|
+
);
|
|
116
|
+
});
|
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file What the proxy promises the browser must be answerable.
|
|
3
|
-
*
|
|
4
|
-
* The session manager asks the planner's media-info cache which tracks the
|
|
5
|
-
* output will carry. That cache never held the codecs — it holds dimensions,
|
|
6
|
-
* duration, fps, start time and an HDR flag — so the read produced `undefined`
|
|
7
|
-
* for both and the declaration came out `{video: false, audio: false}` for
|
|
8
|
-
* EVERY session since the check existed. Measured 2026-08-11 in the field.
|
|
9
|
-
*
|
|
10
|
-
* Nothing caught it: the writer and the reader each looked correct alone, and
|
|
11
|
-
* no test ever compared the shape of what is stored with the shape of what is
|
|
12
|
-
* read. That comparison is this file's whole job.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import test from "node:test";
|
|
16
|
-
import assert from "node:assert/strict";
|
|
17
|
-
import { readFileSync } from "node:fs";
|
|
18
|
-
|
|
19
|
-
const planner = readFileSync(new URL("../services/media/playback-planner.js", import.meta.url), "utf8");
|
|
20
|
-
const sessions = readFileSync(new URL("../services/encode/Renditions.js", import.meta.url), "utf8");
|
|
21
|
-
|
|
22
|
-
test("every field the declaration reads is a field the cache stores", () => {
|
|
23
|
-
// What `declaredTracks` reads off the cached media info.
|
|
24
|
-
const read = [...sessions.matchAll(/probed\?\.([A-Za-z]+)/g)].map((match) => match[1]);
|
|
25
|
-
assert.ok(read.length > 0, "the declaration must read something, or it declares nothing");
|
|
26
|
-
|
|
27
|
-
const stored = planner.slice(planner.indexOf("mediaInfoCache.set("));
|
|
28
|
-
for (const field of new Set(read)) {
|
|
29
|
-
assert.ok(
|
|
30
|
-
stored.includes(`${field}:`),
|
|
31
|
-
`the declaration reads "${field}" and the media-info cache never stores it — ` +
|
|
32
|
-
"the read yields undefined and the promise silently becomes false"
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* @file What the proxy promises the browser must be answerable.
|
|
3
|
+
*
|
|
4
|
+
* The session manager asks the planner's media-info cache which tracks the
|
|
5
|
+
* output will carry. That cache never held the codecs — it holds dimensions,
|
|
6
|
+
* duration, fps, start time and an HDR flag — so the read produced `undefined`
|
|
7
|
+
* for both and the declaration came out `{video: false, audio: false}` for
|
|
8
|
+
* EVERY session since the check existed. Measured 2026-08-11 in the field.
|
|
9
|
+
*
|
|
10
|
+
* Nothing caught it: the writer and the reader each looked correct alone, and
|
|
11
|
+
* no test ever compared the shape of what is stored with the shape of what is
|
|
12
|
+
* read. That comparison is this file's whole job.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import test from "node:test";
|
|
16
|
+
import assert from "node:assert/strict";
|
|
17
|
+
import { readFileSync } from "node:fs";
|
|
18
|
+
|
|
19
|
+
const planner = readFileSync(new URL("../services/media/playback-planner.js", import.meta.url), "utf8");
|
|
20
|
+
const sessions = readFileSync(new URL("../services/encode/Renditions.js", import.meta.url), "utf8");
|
|
21
|
+
|
|
22
|
+
test("every field the declaration reads is a field the cache stores", () => {
|
|
23
|
+
// What `declaredTracks` reads off the cached media info.
|
|
24
|
+
const read = [...sessions.matchAll(/probed\?\.([A-Za-z]+)/g)].map((match) => match[1]);
|
|
25
|
+
assert.ok(read.length > 0, "the declaration must read something, or it declares nothing");
|
|
26
|
+
|
|
27
|
+
const stored = planner.slice(planner.indexOf("mediaInfoCache.set("));
|
|
28
|
+
for (const field of new Set(read)) {
|
|
29
|
+
assert.ok(
|
|
30
|
+
stored.includes(`${field}:`),
|
|
31
|
+
`the declaration reads "${field}" and the media-info cache never stores it — ` +
|
|
32
|
+
"the read yields undefined and the promise silently becomes false"
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
});
|