@torrent-tv/proxy 2.88.2 → 2.88.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 +2259 -2246
- 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 +23 -4
- 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 -29
- package/routes/api/playback-plan/post.js +53 -53
- 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 -217
- 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 +138 -138
- package/routes/api/transcode-sessions/post.js +178 -173
- 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 +380 -380
- package/routes/transcode/audio-file/get.js +64 -64
- package/routes/transcode/audio-warm/get.js +95 -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 +725 -725
- package/services/encode/CoverageMap.js +428 -428
- package/services/encode/CushionReport.js +345 -345
- package/services/encode/EncodeAdmission.js +376 -376
- package/services/encode/EncodeOrchestrator.js +1217 -1217
- package/services/encode/EncodePlan.js +1366 -1366
- package/services/encode/EncodeRun.js +813 -813
- package/services/encode/EncodeRuns.js +1235 -1235
- 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 +783 -758
- package/services/encode/OutputTimes.js +579 -579
- package/services/encode/QsvEncoder.js +58 -58
- package/services/encode/Renditions.js +2282 -2184
- 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 +418 -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 +1150 -1141
- 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 +349 -254
- package/services/encode/quality/output-format.js +319 -315
- 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 +754 -754
- package/services/encode/run-costs.js +155 -155
- package/services/encode/run-inputs.js +42 -42
- package/services/encode/segment-formats/fmp4.js +456 -456
- package/services/encode/segment-formats/index.js +85 -85
- package/services/encode/segment-formats/init-compat.js +324 -324
- package/services/encode/segment-formats/mp4-boxes.js +21 -6
- 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 +298 -245
- package/services/media/container/AviContainer.js +266 -266
- package/services/media/container/Container.js +609 -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 +1344 -1244
- 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 +931 -931
- package/services/media/tracks/AudioTrack.js +143 -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/audio-bound.js +233 -0
- 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 +1150 -1150
- package/services/server/ViewerRequests.js +760 -760
- package/services/server/WaitLedger.js +142 -142
- package/services/server/controllers/PlaybackController.js +40 -40
- package/services/server/controllers/SubtitleController.js +109 -109
- package/services/server/http-responses.js +60 -60
- package/services/server/playback-readiness.js +13 -8
- package/services/server/transcode-session-files.js +325 -325
- package/services/server/wire-outputs.js +995 -992
- 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 +539 -539
- package/services/storage/piece-store/piece-lru.js +495 -495
- package/services/storage/piece-store/shared-piece-store.js +1931 -1931
- package/services/storage/returns.js +103 -103
- package/services/storage/segment-store/SegmentStore.js +1240 -1240
- package/services/storage/wire.js +175 -175
- 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/episode-naming.js +182 -182
- 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 -2675
- package/services/torrent/worker/background-fill.js +213 -213
- package/services/torrent/worker/channel.js +271 -271
- package/services/torrent/worker/client.js +765 -765
- 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 +371 -371
- package/services/torrent/worker/protocol.js +184 -184
- 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 -1129
- package/services/transport/data-channel-handler.js +1369 -1369
- 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 +691 -691
- 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-buffer-continuity.test.js +70 -0
- package/test/audio-inventory.test.js +212 -212
- 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/data-channel-viewer-lifecycle.test.js +92 -92
- package/test/dead-torrent-handle.test.js +52 -52
- package/test/declared-rates.test.js +116 -116
- package/test/declared-tracks.test.js +43 -43
- 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 +162 -162
- 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/episode-naming.test.js +131 -131
- 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/fixtures/audio-frame-boundary.json +1 -0
- package/test/fmp4-presentation-clock.test.js +147 -147
- 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 +112 -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 +270 -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 +401 -401
- 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 +595 -595
- package/test/piece-store-never-refuses.test.js +153 -153
- package/test/piece-store-reservations.test.js +403 -403
- 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/playback-readiness.test.js +453 -453
- package/test/playback-start-regressions.test.js +99 -99
- 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 +1257 -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 +105 -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/soundtrack-rate-bound.test.js +289 -0
- package/test/source-file.test.js +133 -133
- package/test/source-files-route.test.js +113 -113
- 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 +209 -209
- 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 +504 -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/utils/torrent-source-key.js +38 -38
|
@@ -1,125 +1,125 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file How many bits of film there are per second of playback.
|
|
3
|
-
*
|
|
4
|
-
* A fact about the OUTPUT, and the two figures the HLS specification asks for:
|
|
5
|
-
* the average over the whole variant, and the peak one segment reaches. Both
|
|
6
|
-
* are measured — the average from the file's own length and duration, the peak
|
|
7
|
-
* from the biggest piece actually produced and the span it covers.
|
|
8
|
-
*
|
|
9
|
-
* It used to be `height * height * 3.2`, a rule of thumb, justified by a comment
|
|
10
|
-
* saying the measurement was not available before encoding starts. It is: the
|
|
11
|
-
* length and the duration are known when the session is created, and the peak
|
|
12
|
-
* refines itself as pieces appear. Field 2026-09-08: 3.73 Mbit/s declared for a
|
|
13
|
-
* file carrying 18.4, and the browser's cushion is sized in BYTES from that
|
|
14
|
-
* figure — 120 s asked bought 26 s of film.
|
|
15
|
-
*
|
|
16
|
-
* Pure: plain numbers in, plain numbers out. Nothing here knows what a session,
|
|
17
|
-
* a store or a torrent is.
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* The two rates to declare, from what has been measured of this file.
|
|
22
|
-
*
|
|
23
|
-
* @param {object} params
|
|
24
|
-
* @param {number} params.fileLength - Bytes of the source file, 0 until the
|
|
25
|
-
* torrent has said. Counts every track and every byte of container, so it is
|
|
26
|
-
* the better figure of the two.
|
|
27
|
-
* @param {number} params.durationSeconds
|
|
28
|
-
* @param {number} [params.streamBitsPerSecond] - What a probe read off the
|
|
29
|
-
* video stream, known from session creation. Lower than the whole file's rate
|
|
30
|
-
* because it is one track of it, and used while the length is not known.
|
|
31
|
-
* @param {{ index: number, size: number }} [params.largest] - The biggest piece
|
|
32
|
-
* produced so far, with its number. An index of `-1` means none yet.
|
|
33
|
-
* @param {number[]} [params.boundaries] - Where the file is cut, so the biggest
|
|
34
|
-
* piece's own span is known. Bytes alone cannot give a rate.
|
|
35
|
-
* @returns {{ averageBitsPerSecond: number, peakOverAverage: number }}
|
|
36
|
-
*/
|
|
37
|
-
export function declaredRates({
|
|
38
|
-
fileLength,
|
|
39
|
-
durationSeconds,
|
|
40
|
-
streamBitsPerSecond = 0,
|
|
41
|
-
largest = null,
|
|
42
|
-
boundaries = null
|
|
43
|
-
}) {
|
|
44
|
-
const length = Number(fileLength);
|
|
45
|
-
const duration = Number(durationSeconds);
|
|
46
|
-
// TWO SOURCES, AND THE LARGER OF THEM, because each is measured and each can
|
|
47
|
-
// be absent — and absent is 0, so the larger is whichever was measured.
|
|
48
|
-
//
|
|
49
|
-
// - the whole file's length over its duration: the best figure, since it
|
|
50
|
-
// counts every track and every byte of container. Known only once the
|
|
51
|
-
// torrent has reported the file's size;
|
|
52
|
-
// - what the probe read off the video stream: known when the session is
|
|
53
|
-
// created, and lower, because it is one track of several.
|
|
54
|
-
//
|
|
55
|
-
// The first alone was what shipped in 2.80.14, read from a field that does
|
|
56
|
-
// not exist on a source file. It came back 0 on every session, so every
|
|
57
|
-
// variant was declared at the 400 kbit/s floor — nine times WORSE than the
|
|
58
|
-
// rule of thumb it replaced — and the browser, which sizes its cushion in
|
|
59
|
-
// bytes from this figure, held 0.1 s of film against 120 s asked. The picture
|
|
60
|
-
// stood still for 116.7 s of one viewing. Assuming a field instead of
|
|
61
|
-
// checking it is the whole of that fault.
|
|
62
|
-
const fromLength = length > 0 && duration > 0 ? (length * 8) / duration : 0;
|
|
63
|
-
const averageBitsPerSecond = Math.max(fromLength, Number(streamBitsPerSecond) || 0);
|
|
64
|
-
if (!(averageBitsPerSecond > 0)) {
|
|
65
|
-
return { averageBitsPerSecond: 0, peakOverAverage: 1 };
|
|
66
|
-
}
|
|
67
|
-
const index = Number(largest?.index);
|
|
68
|
-
const size = Number(largest?.size);
|
|
69
|
-
if (!Number.isInteger(index) || index < 0 || !(size > 0) || !Array.isArray(boundaries)) {
|
|
70
|
-
// Nothing has been produced, so the peak is not yet a measured quantity and
|
|
71
|
-
// is declared equal to the average. It rises as soon as one piece exists,
|
|
72
|
-
// and a ratio invented meanwhile would be exactly the fabrication this file
|
|
73
|
-
// replaced.
|
|
74
|
-
return { averageBitsPerSecond, peakOverAverage: 1 };
|
|
75
|
-
}
|
|
76
|
-
const span = Number(boundaries[index + 1]) - Number(boundaries[index]);
|
|
77
|
-
if (!(span > 0)) {
|
|
78
|
-
return { averageBitsPerSecond, peakOverAverage: 1 };
|
|
79
|
-
}
|
|
80
|
-
// Never below one: the peak cannot be under the average, and a piece that
|
|
81
|
-
// happens to be the smallest in a short session must not lower the figure the
|
|
82
|
-
// player sizes its cushion from.
|
|
83
|
-
return {
|
|
84
|
-
averageBitsPerSecond,
|
|
85
|
-
peakOverAverage: Math.max(1, ((size * 8) / span) / averageBitsPerSecond)
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* The three arguments the master playlist needs to declare its rates.
|
|
91
|
-
*
|
|
92
|
-
* Assembled here rather than at the call site, because all three are facts
|
|
93
|
-
* about the OUTPUT and none of them is a fact about a session: the file's own
|
|
94
|
-
* length and duration, the biggest piece made of it, and the cap imposed on the
|
|
95
|
-
* one height being produced. The caller holds them and passes them as numbers.
|
|
96
|
-
*
|
|
97
|
-
* Only the height being produced has a cap — the other heights do not exist
|
|
98
|
-
* yet, and stating one for them would be a guess about a session nobody has
|
|
99
|
-
* made.
|
|
100
|
-
*
|
|
101
|
-
* @param {object} params
|
|
102
|
-
* @param {number} params.fileLength
|
|
103
|
-
* @param {number} params.durationSeconds
|
|
104
|
-
* @param {number} [params.streamBitsPerSecond]
|
|
105
|
-
* @param {{ index: number, size: number } | null} [params.largest]
|
|
106
|
-
* @param {number[] | null} [params.boundaries]
|
|
107
|
-
* @param {number} [params.producedHeight] - The height this output encodes at.
|
|
108
|
-
* @param {number} [params.capKbps] - What that height is capped at, if it is.
|
|
109
|
-
* @returns {{ averageBitsPerSecond: number, peakOverAverage: number,
|
|
110
|
-
* capKbpsFor: (height: number) => number }}
|
|
111
|
-
*/
|
|
112
|
-
export function masterRateArgs({
|
|
113
|
-
fileLength,
|
|
114
|
-
durationSeconds,
|
|
115
|
-
streamBitsPerSecond = 0,
|
|
116
|
-
largest = null,
|
|
117
|
-
boundaries = null,
|
|
118
|
-
producedHeight = 0,
|
|
119
|
-
capKbps = 0
|
|
120
|
-
}) {
|
|
121
|
-
return {
|
|
122
|
-
...declaredRates({ fileLength, durationSeconds, streamBitsPerSecond, largest, boundaries }),
|
|
123
|
-
capKbpsFor: (height) => (height === producedHeight ? capKbps : 0)
|
|
124
|
-
};
|
|
125
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* @file How many bits of film there are per second of playback.
|
|
3
|
+
*
|
|
4
|
+
* A fact about the OUTPUT, and the two figures the HLS specification asks for:
|
|
5
|
+
* the average over the whole variant, and the peak one segment reaches. Both
|
|
6
|
+
* are measured — the average from the file's own length and duration, the peak
|
|
7
|
+
* from the biggest piece actually produced and the span it covers.
|
|
8
|
+
*
|
|
9
|
+
* It used to be `height * height * 3.2`, a rule of thumb, justified by a comment
|
|
10
|
+
* saying the measurement was not available before encoding starts. It is: the
|
|
11
|
+
* length and the duration are known when the session is created, and the peak
|
|
12
|
+
* refines itself as pieces appear. Field 2026-09-08: 3.73 Mbit/s declared for a
|
|
13
|
+
* file carrying 18.4, and the browser's cushion is sized in BYTES from that
|
|
14
|
+
* figure — 120 s asked bought 26 s of film.
|
|
15
|
+
*
|
|
16
|
+
* Pure: plain numbers in, plain numbers out. Nothing here knows what a session,
|
|
17
|
+
* a store or a torrent is.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The two rates to declare, from what has been measured of this file.
|
|
22
|
+
*
|
|
23
|
+
* @param {object} params
|
|
24
|
+
* @param {number} params.fileLength - Bytes of the source file, 0 until the
|
|
25
|
+
* torrent has said. Counts every track and every byte of container, so it is
|
|
26
|
+
* the better figure of the two.
|
|
27
|
+
* @param {number} params.durationSeconds
|
|
28
|
+
* @param {number} [params.streamBitsPerSecond] - What a probe read off the
|
|
29
|
+
* video stream, known from session creation. Lower than the whole file's rate
|
|
30
|
+
* because it is one track of it, and used while the length is not known.
|
|
31
|
+
* @param {{ index: number, size: number }} [params.largest] - The biggest piece
|
|
32
|
+
* produced so far, with its number. An index of `-1` means none yet.
|
|
33
|
+
* @param {number[]} [params.boundaries] - Where the file is cut, so the biggest
|
|
34
|
+
* piece's own span is known. Bytes alone cannot give a rate.
|
|
35
|
+
* @returns {{ averageBitsPerSecond: number, peakOverAverage: number }}
|
|
36
|
+
*/
|
|
37
|
+
export function declaredRates({
|
|
38
|
+
fileLength,
|
|
39
|
+
durationSeconds,
|
|
40
|
+
streamBitsPerSecond = 0,
|
|
41
|
+
largest = null,
|
|
42
|
+
boundaries = null
|
|
43
|
+
}) {
|
|
44
|
+
const length = Number(fileLength);
|
|
45
|
+
const duration = Number(durationSeconds);
|
|
46
|
+
// TWO SOURCES, AND THE LARGER OF THEM, because each is measured and each can
|
|
47
|
+
// be absent — and absent is 0, so the larger is whichever was measured.
|
|
48
|
+
//
|
|
49
|
+
// - the whole file's length over its duration: the best figure, since it
|
|
50
|
+
// counts every track and every byte of container. Known only once the
|
|
51
|
+
// torrent has reported the file's size;
|
|
52
|
+
// - what the probe read off the video stream: known when the session is
|
|
53
|
+
// created, and lower, because it is one track of several.
|
|
54
|
+
//
|
|
55
|
+
// The first alone was what shipped in 2.80.14, read from a field that does
|
|
56
|
+
// not exist on a source file. It came back 0 on every session, so every
|
|
57
|
+
// variant was declared at the 400 kbit/s floor — nine times WORSE than the
|
|
58
|
+
// rule of thumb it replaced — and the browser, which sizes its cushion in
|
|
59
|
+
// bytes from this figure, held 0.1 s of film against 120 s asked. The picture
|
|
60
|
+
// stood still for 116.7 s of one viewing. Assuming a field instead of
|
|
61
|
+
// checking it is the whole of that fault.
|
|
62
|
+
const fromLength = length > 0 && duration > 0 ? (length * 8) / duration : 0;
|
|
63
|
+
const averageBitsPerSecond = Math.max(fromLength, Number(streamBitsPerSecond) || 0);
|
|
64
|
+
if (!(averageBitsPerSecond > 0)) {
|
|
65
|
+
return { averageBitsPerSecond: 0, peakOverAverage: 1 };
|
|
66
|
+
}
|
|
67
|
+
const index = Number(largest?.index);
|
|
68
|
+
const size = Number(largest?.size);
|
|
69
|
+
if (!Number.isInteger(index) || index < 0 || !(size > 0) || !Array.isArray(boundaries)) {
|
|
70
|
+
// Nothing has been produced, so the peak is not yet a measured quantity and
|
|
71
|
+
// is declared equal to the average. It rises as soon as one piece exists,
|
|
72
|
+
// and a ratio invented meanwhile would be exactly the fabrication this file
|
|
73
|
+
// replaced.
|
|
74
|
+
return { averageBitsPerSecond, peakOverAverage: 1 };
|
|
75
|
+
}
|
|
76
|
+
const span = Number(boundaries[index + 1]) - Number(boundaries[index]);
|
|
77
|
+
if (!(span > 0)) {
|
|
78
|
+
return { averageBitsPerSecond, peakOverAverage: 1 };
|
|
79
|
+
}
|
|
80
|
+
// Never below one: the peak cannot be under the average, and a piece that
|
|
81
|
+
// happens to be the smallest in a short session must not lower the figure the
|
|
82
|
+
// player sizes its cushion from.
|
|
83
|
+
return {
|
|
84
|
+
averageBitsPerSecond,
|
|
85
|
+
peakOverAverage: Math.max(1, ((size * 8) / span) / averageBitsPerSecond)
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The three arguments the master playlist needs to declare its rates.
|
|
91
|
+
*
|
|
92
|
+
* Assembled here rather than at the call site, because all three are facts
|
|
93
|
+
* about the OUTPUT and none of them is a fact about a session: the file's own
|
|
94
|
+
* length and duration, the biggest piece made of it, and the cap imposed on the
|
|
95
|
+
* one height being produced. The caller holds them and passes them as numbers.
|
|
96
|
+
*
|
|
97
|
+
* Only the height being produced has a cap — the other heights do not exist
|
|
98
|
+
* yet, and stating one for them would be a guess about a session nobody has
|
|
99
|
+
* made.
|
|
100
|
+
*
|
|
101
|
+
* @param {object} params
|
|
102
|
+
* @param {number} params.fileLength
|
|
103
|
+
* @param {number} params.durationSeconds
|
|
104
|
+
* @param {number} [params.streamBitsPerSecond]
|
|
105
|
+
* @param {{ index: number, size: number } | null} [params.largest]
|
|
106
|
+
* @param {number[] | null} [params.boundaries]
|
|
107
|
+
* @param {number} [params.producedHeight] - The height this output encodes at.
|
|
108
|
+
* @param {number} [params.capKbps] - What that height is capped at, if it is.
|
|
109
|
+
* @returns {{ averageBitsPerSecond: number, peakOverAverage: number,
|
|
110
|
+
* capKbpsFor: (height: number) => number }}
|
|
111
|
+
*/
|
|
112
|
+
export function masterRateArgs({
|
|
113
|
+
fileLength,
|
|
114
|
+
durationSeconds,
|
|
115
|
+
streamBitsPerSecond = 0,
|
|
116
|
+
largest = null,
|
|
117
|
+
boundaries = null,
|
|
118
|
+
producedHeight = 0,
|
|
119
|
+
capKbps = 0
|
|
120
|
+
}) {
|
|
121
|
+
return {
|
|
122
|
+
...declaredRates({ fileLength, durationSeconds, streamBitsPerSecond, largest, boundaries }),
|
|
123
|
+
capKbpsFor: (height) => (height === producedHeight ? capKbps : 0)
|
|
124
|
+
};
|
|
125
|
+
}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file Which container an output's segments are in, read from its key.
|
|
3
|
-
*
|
|
4
|
-
* The key names the format produced, so a directory of segments can be read
|
|
5
|
-
* back without any record kept elsewhere — which is what adopting segments an
|
|
6
|
-
* earlier process left behind needs. A key in a shape this version does not
|
|
7
|
-
* write (one naming the box a viewer asked for rather than the format
|
|
8
|
-
* produced) cannot say what is inside, and the answer is null.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { OutputSpec } from "./output/index.js";
|
|
12
|
-
import { resolveSegmentFormat, SEGMENT_FORMAT_IDS } from "./segment-formats/index.js";
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* @param {string} key
|
|
16
|
-
* @returns {object | null} The segment format, or null when the key cannot say.
|
|
17
|
-
*/
|
|
18
|
-
export function segmentFormatOfKey(key) {
|
|
19
|
-
const stated = OutputSpec.fromKey(key)?.segmentFormatId ?? "";
|
|
20
|
-
return SEGMENT_FORMAT_IDS.includes(stated) ? resolveSegmentFormat(stated) : null;
|
|
21
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* @file Which container an output's segments are in, read from its key.
|
|
3
|
+
*
|
|
4
|
+
* The key names the format produced, so a directory of segments can be read
|
|
5
|
+
* back without any record kept elsewhere — which is what adopting segments an
|
|
6
|
+
* earlier process left behind needs. A key in a shape this version does not
|
|
7
|
+
* write (one naming the box a viewer asked for rather than the format
|
|
8
|
+
* produced) cannot say what is inside, and the answer is null.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { OutputSpec } from "./output/index.js";
|
|
12
|
+
import { resolveSegmentFormat, SEGMENT_FORMAT_IDS } from "./segment-formats/index.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {string} key
|
|
16
|
+
* @returns {object | null} The segment format, or null when the key cannot say.
|
|
17
|
+
*/
|
|
18
|
+
export function segmentFormatOfKey(key) {
|
|
19
|
+
const stated = OutputSpec.fromKey(key)?.segmentFormatId ?? "";
|
|
20
|
+
return SEGMENT_FORMAT_IDS.includes(stated) ? resolveSegmentFormat(stated) : null;
|
|
21
|
+
}
|