@torrent-tv/proxy 2.87.21 → 2.88.1
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 +2242 -2234
- 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 +1 -1
- 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/audio-tracks/post.js +29 -29
- package/routes/api/playback-plan/post.js +53 -53
- package/routes/api/sources/files/get.js +106 -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 -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 +173 -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 +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 +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 +758 -758
- package/services/encode/OutputTimes.js +579 -579
- package/services/encode/QsvEncoder.js +58 -58
- package/services/encode/Renditions.js +2184 -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 +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 +1141 -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 +254 -254
- package/services/encode/quality/output-format.js +315 -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 +515 -515
- 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 -245
- 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 +339 -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 +931 -931
- 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 +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 +696 -696
- package/services/server/transcode-session-files.js +325 -325
- package/services/server/wire-outputs.js +992 -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/Contents.js +382 -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/episode-naming.js +182 -0
- 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-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 -0
- 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-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 +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 +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 +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 +12 -2
- 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-metadata.test.js +15 -0
- 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/utils/torrent-source-key.js +38 -38
|
@@ -1,140 +1,140 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file The one place bytes become piece numbers.
|
|
3
|
-
*
|
|
4
|
-
* Everything else counts in bytes. A piece is what the protocol hashes and what
|
|
5
|
-
* memory is allocated in, and nothing outside these functions should have to
|
|
6
|
-
* know its length. Before 2026-09-02 the division was done in three places —
|
|
7
|
-
* the memory allowance, the reader's window, the eviction ceiling — each
|
|
8
|
-
* rounding down on its own, and the rounding was where a real failure lived:
|
|
9
|
-
* with 16 MiB pieces a 64 MB allowance is four places while two readers asking
|
|
10
|
-
* for 96 MB each want six, and the shortage was invisible because both figures
|
|
11
|
-
* had already been floored.
|
|
12
|
-
*
|
|
13
|
-
* Pure arithmetic on numbers: no torrent, no file object, nothing to stub.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* The pieces that hold a byte range of a file.
|
|
18
|
-
*
|
|
19
|
-
* Inclusive at both ends, because a byte range that ends inside a piece still
|
|
20
|
-
* needs that whole piece: the protocol delivers and verifies nothing smaller.
|
|
21
|
-
*
|
|
22
|
-
* @param {object} params
|
|
23
|
-
* @param {number} params.fileOffset - Where the file starts within the torrent.
|
|
24
|
-
* @param {number} params.byteStart - First byte wanted, relative to the file.
|
|
25
|
-
* @param {number} params.byteEnd - Last byte wanted, relative to the file.
|
|
26
|
-
* @param {number} params.pieceLength
|
|
27
|
-
* @returns {{ from: number, to: number } | null} Null when the range is not a
|
|
28
|
-
* range, or the piece length is not usable.
|
|
29
|
-
*/
|
|
30
|
-
export function piecesOf({ fileOffset, byteStart, byteEnd, pieceLength }) {
|
|
31
|
-
if (!Number.isFinite(pieceLength) || pieceLength <= 0) {
|
|
32
|
-
return null;
|
|
33
|
-
}
|
|
34
|
-
if (!Number.isFinite(fileOffset) || fileOffset < 0) {
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
if (!Number.isFinite(byteStart) || !Number.isFinite(byteEnd) || byteEnd < byteStart) {
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
return {
|
|
41
|
-
from: Math.floor((fileOffset + Math.max(0, byteStart)) / pieceLength),
|
|
42
|
-
to: Math.floor((fileOffset + byteEnd) / pieceLength)
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* The bytes of a file that a piece range covers, clamped to the file.
|
|
48
|
-
*
|
|
49
|
-
* The inverse of {@link piecesOf}, and here for the same reason: a caller that
|
|
50
|
-
* still thinks in pieces — the reader, which walks a file piece by piece
|
|
51
|
-
* because that is what arrives — states its need in bytes like everyone else,
|
|
52
|
-
* and the conversion stays in this one file rather than being written out
|
|
53
|
-
* again at the boundary.
|
|
54
|
-
*
|
|
55
|
-
* @param {object} params
|
|
56
|
-
* @param {number} params.fileOffset - Where the file starts within the torrent.
|
|
57
|
-
* @param {number} params.fileLength
|
|
58
|
-
* @param {number} params.from - First piece, inclusive.
|
|
59
|
-
* @param {number} params.to - Last piece, inclusive.
|
|
60
|
-
* @param {number} params.pieceLength
|
|
61
|
-
* @returns {{ byteStart: number, byteEnd: number } | null}
|
|
62
|
-
*/
|
|
63
|
-
export function bytesOf({ fileOffset, fileLength, from, to, pieceLength }) {
|
|
64
|
-
if (!Number.isFinite(pieceLength) || pieceLength <= 0) {
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
|
-
if (!Number.isFinite(fileOffset) || !Number.isFinite(fileLength) || fileLength <= 0) {
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
if (!Number.isInteger(from) || !Number.isInteger(to) || to < from) {
|
|
71
|
-
return null;
|
|
72
|
-
}
|
|
73
|
-
const byteStart = Math.max(0, from * pieceLength - fileOffset);
|
|
74
|
-
const byteEnd = Math.min(fileLength - 1, (to + 1) * pieceLength - 1 - fileOffset);
|
|
75
|
-
return byteEnd < byteStart ? null : { byteStart, byteEnd };
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* How many pieces a number of bytes needs, at worst.
|
|
80
|
-
*
|
|
81
|
-
* At worst, because a range of one byte can still straddle two pieces. Used
|
|
82
|
-
* where a budget in bytes has to be turned into places, and rounding the other
|
|
83
|
-
* way would promise room that is not there.
|
|
84
|
-
*
|
|
85
|
-
* @param {number} bytes
|
|
86
|
-
* @param {number} pieceLength
|
|
87
|
-
* @returns {number}
|
|
88
|
-
*/
|
|
89
|
-
export function piecesNeededFor(bytes, pieceLength) {
|
|
90
|
-
if (!Number.isFinite(pieceLength) || pieceLength <= 0 || !Number.isFinite(bytes) || bytes <= 0) {
|
|
91
|
-
return 0;
|
|
92
|
-
}
|
|
93
|
-
return Math.ceil(bytes / pieceLength) + 1;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* How many whole pieces fit in a number of bytes.
|
|
98
|
-
*
|
|
99
|
-
* The other direction, and it rounds DOWN: a budget buys only the places it can
|
|
100
|
-
* pay for in full.
|
|
101
|
-
*
|
|
102
|
-
* @param {number} bytes
|
|
103
|
-
* @param {number} pieceLength
|
|
104
|
-
* @returns {number}
|
|
105
|
-
*/
|
|
106
|
-
export function piecesWithin(bytes, pieceLength) {
|
|
107
|
-
if (!Number.isFinite(pieceLength) || pieceLength <= 0 || !Number.isFinite(bytes) || bytes <= 0) {
|
|
108
|
-
return 0;
|
|
109
|
-
}
|
|
110
|
-
return Math.floor(bytes / pieceLength);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Split a byte range into a few ranges, nearest to a point first.
|
|
115
|
-
*
|
|
116
|
-
* For the gap behind the playhead. WebTorrent walks a selection from its start
|
|
117
|
-
* upwards — `for (piece = next.from + next.offset; piece <= next.to; piece++)` —
|
|
118
|
-
* so one claim over everything behind the viewer would be fetched from the
|
|
119
|
-
* beginning of the file, which is the end furthest from where a backward seek
|
|
120
|
-
* would land. Split, and the part nearest the playhead is stated first.
|
|
121
|
-
*
|
|
122
|
-
* @param {object} params
|
|
123
|
-
* @param {number} params.byteStart - First byte of the gap.
|
|
124
|
-
* @param {number} params.byteEnd - Last byte of the gap, nearest the playhead.
|
|
125
|
-
* @param {number} params.parts - How many ranges to split into.
|
|
126
|
-
* @returns {Array<{ byteStart: number, byteEnd: number }>} Nearest first.
|
|
127
|
-
*/
|
|
128
|
-
export function nearestFirst({ byteStart, byteEnd, parts }) {
|
|
129
|
-
if (!Number.isFinite(byteStart) || !Number.isFinite(byteEnd) || byteEnd < byteStart) {
|
|
130
|
-
return [];
|
|
131
|
-
}
|
|
132
|
-
const count = Number.isInteger(parts) && parts > 0 ? parts : 1;
|
|
133
|
-
const total = byteEnd - byteStart + 1;
|
|
134
|
-
const each = Math.ceil(total / count);
|
|
135
|
-
const ranges = [];
|
|
136
|
-
for (let end = byteEnd; end >= byteStart; end -= each) {
|
|
137
|
-
ranges.push({ byteStart: Math.max(byteStart, end - each + 1), byteEnd: end });
|
|
138
|
-
}
|
|
139
|
-
return ranges;
|
|
140
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* @file The one place bytes become piece numbers.
|
|
3
|
+
*
|
|
4
|
+
* Everything else counts in bytes. A piece is what the protocol hashes and what
|
|
5
|
+
* memory is allocated in, and nothing outside these functions should have to
|
|
6
|
+
* know its length. Before 2026-09-02 the division was done in three places —
|
|
7
|
+
* the memory allowance, the reader's window, the eviction ceiling — each
|
|
8
|
+
* rounding down on its own, and the rounding was where a real failure lived:
|
|
9
|
+
* with 16 MiB pieces a 64 MB allowance is four places while two readers asking
|
|
10
|
+
* for 96 MB each want six, and the shortage was invisible because both figures
|
|
11
|
+
* had already been floored.
|
|
12
|
+
*
|
|
13
|
+
* Pure arithmetic on numbers: no torrent, no file object, nothing to stub.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The pieces that hold a byte range of a file.
|
|
18
|
+
*
|
|
19
|
+
* Inclusive at both ends, because a byte range that ends inside a piece still
|
|
20
|
+
* needs that whole piece: the protocol delivers and verifies nothing smaller.
|
|
21
|
+
*
|
|
22
|
+
* @param {object} params
|
|
23
|
+
* @param {number} params.fileOffset - Where the file starts within the torrent.
|
|
24
|
+
* @param {number} params.byteStart - First byte wanted, relative to the file.
|
|
25
|
+
* @param {number} params.byteEnd - Last byte wanted, relative to the file.
|
|
26
|
+
* @param {number} params.pieceLength
|
|
27
|
+
* @returns {{ from: number, to: number } | null} Null when the range is not a
|
|
28
|
+
* range, or the piece length is not usable.
|
|
29
|
+
*/
|
|
30
|
+
export function piecesOf({ fileOffset, byteStart, byteEnd, pieceLength }) {
|
|
31
|
+
if (!Number.isFinite(pieceLength) || pieceLength <= 0) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
if (!Number.isFinite(fileOffset) || fileOffset < 0) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
if (!Number.isFinite(byteStart) || !Number.isFinite(byteEnd) || byteEnd < byteStart) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
from: Math.floor((fileOffset + Math.max(0, byteStart)) / pieceLength),
|
|
42
|
+
to: Math.floor((fileOffset + byteEnd) / pieceLength)
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The bytes of a file that a piece range covers, clamped to the file.
|
|
48
|
+
*
|
|
49
|
+
* The inverse of {@link piecesOf}, and here for the same reason: a caller that
|
|
50
|
+
* still thinks in pieces — the reader, which walks a file piece by piece
|
|
51
|
+
* because that is what arrives — states its need in bytes like everyone else,
|
|
52
|
+
* and the conversion stays in this one file rather than being written out
|
|
53
|
+
* again at the boundary.
|
|
54
|
+
*
|
|
55
|
+
* @param {object} params
|
|
56
|
+
* @param {number} params.fileOffset - Where the file starts within the torrent.
|
|
57
|
+
* @param {number} params.fileLength
|
|
58
|
+
* @param {number} params.from - First piece, inclusive.
|
|
59
|
+
* @param {number} params.to - Last piece, inclusive.
|
|
60
|
+
* @param {number} params.pieceLength
|
|
61
|
+
* @returns {{ byteStart: number, byteEnd: number } | null}
|
|
62
|
+
*/
|
|
63
|
+
export function bytesOf({ fileOffset, fileLength, from, to, pieceLength }) {
|
|
64
|
+
if (!Number.isFinite(pieceLength) || pieceLength <= 0) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
if (!Number.isFinite(fileOffset) || !Number.isFinite(fileLength) || fileLength <= 0) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
if (!Number.isInteger(from) || !Number.isInteger(to) || to < from) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
const byteStart = Math.max(0, from * pieceLength - fileOffset);
|
|
74
|
+
const byteEnd = Math.min(fileLength - 1, (to + 1) * pieceLength - 1 - fileOffset);
|
|
75
|
+
return byteEnd < byteStart ? null : { byteStart, byteEnd };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* How many pieces a number of bytes needs, at worst.
|
|
80
|
+
*
|
|
81
|
+
* At worst, because a range of one byte can still straddle two pieces. Used
|
|
82
|
+
* where a budget in bytes has to be turned into places, and rounding the other
|
|
83
|
+
* way would promise room that is not there.
|
|
84
|
+
*
|
|
85
|
+
* @param {number} bytes
|
|
86
|
+
* @param {number} pieceLength
|
|
87
|
+
* @returns {number}
|
|
88
|
+
*/
|
|
89
|
+
export function piecesNeededFor(bytes, pieceLength) {
|
|
90
|
+
if (!Number.isFinite(pieceLength) || pieceLength <= 0 || !Number.isFinite(bytes) || bytes <= 0) {
|
|
91
|
+
return 0;
|
|
92
|
+
}
|
|
93
|
+
return Math.ceil(bytes / pieceLength) + 1;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* How many whole pieces fit in a number of bytes.
|
|
98
|
+
*
|
|
99
|
+
* The other direction, and it rounds DOWN: a budget buys only the places it can
|
|
100
|
+
* pay for in full.
|
|
101
|
+
*
|
|
102
|
+
* @param {number} bytes
|
|
103
|
+
* @param {number} pieceLength
|
|
104
|
+
* @returns {number}
|
|
105
|
+
*/
|
|
106
|
+
export function piecesWithin(bytes, pieceLength) {
|
|
107
|
+
if (!Number.isFinite(pieceLength) || pieceLength <= 0 || !Number.isFinite(bytes) || bytes <= 0) {
|
|
108
|
+
return 0;
|
|
109
|
+
}
|
|
110
|
+
return Math.floor(bytes / pieceLength);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Split a byte range into a few ranges, nearest to a point first.
|
|
115
|
+
*
|
|
116
|
+
* For the gap behind the playhead. WebTorrent walks a selection from its start
|
|
117
|
+
* upwards — `for (piece = next.from + next.offset; piece <= next.to; piece++)` —
|
|
118
|
+
* so one claim over everything behind the viewer would be fetched from the
|
|
119
|
+
* beginning of the file, which is the end furthest from where a backward seek
|
|
120
|
+
* would land. Split, and the part nearest the playhead is stated first.
|
|
121
|
+
*
|
|
122
|
+
* @param {object} params
|
|
123
|
+
* @param {number} params.byteStart - First byte of the gap.
|
|
124
|
+
* @param {number} params.byteEnd - Last byte of the gap, nearest the playhead.
|
|
125
|
+
* @param {number} params.parts - How many ranges to split into.
|
|
126
|
+
* @returns {Array<{ byteStart: number, byteEnd: number }>} Nearest first.
|
|
127
|
+
*/
|
|
128
|
+
export function nearestFirst({ byteStart, byteEnd, parts }) {
|
|
129
|
+
if (!Number.isFinite(byteStart) || !Number.isFinite(byteEnd) || byteEnd < byteStart) {
|
|
130
|
+
return [];
|
|
131
|
+
}
|
|
132
|
+
const count = Number.isInteger(parts) && parts > 0 ? parts : 1;
|
|
133
|
+
const total = byteEnd - byteStart + 1;
|
|
134
|
+
const each = Math.ceil(total / count);
|
|
135
|
+
const ranges = [];
|
|
136
|
+
for (let end = byteEnd; end >= byteStart; end -= each) {
|
|
137
|
+
ranges.push({ byteStart: Math.max(byteStart, end - each + 1), byteEnd: end });
|
|
138
|
+
}
|
|
139
|
+
return ranges;
|
|
140
|
+
}
|