@torrent-tv/proxy 2.87.3 → 2.87.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/commands/opsx/apply.md +155 -155
- package/.claude/commands/opsx/archive.md +160 -160
- package/.claude/commands/opsx/explore.md +174 -174
- package/.claude/commands/opsx/propose.md +109 -109
- package/.claude/commands/opsx/sync.md +143 -143
- package/.claude/skills/openspec-apply-change/SKILL.md +159 -159
- package/.claude/skills/openspec-archive-change/SKILL.md +117 -117
- package/.claude/skills/openspec-explore/SKILL.md +289 -289
- package/.claude/skills/openspec-propose/SKILL.md +113 -113
- package/.claude/skills/openspec-sync-specs/SKILL.md +147 -147
- package/.dockerignore +5 -5
- package/AGENTS.md +322 -322
- package/CHANGELOG.md +2157 -2153
- package/Dockerfile +30 -30
- package/LICENSE +16 -16
- package/README.md +369 -369
- package/assets/calibration/NOTICE.md +100 -100
- package/assets/diagnostics/sctpstate.gdb +31 -31
- package/bin/cli.js +684 -684
- package/bin/thread-pool.js +30 -30
- package/biome.json +411 -411
- package/docs/browser-proxy-contract.md +94 -94
- package/docs/container-architecture.md +218 -218
- package/docs/download-architecture.md +261 -261
- package/docs/encode-architecture.md +729 -729
- package/docs/encode-run-state.md +96 -96
- package/docs/logs.md +97 -97
- package/docs/socks5-egress-research.md +114 -114
- package/docs/storage-architecture.md +267 -267
- package/knip.json +14 -14
- package/openspec/changes/adaptive-bitrate/design.md +92 -92
- package/openspec/changes/adaptive-bitrate/proposal.md +56 -56
- package/openspec/changes/adaptive-bitrate/specs/adaptive-bitrate/spec.md +51 -51
- package/openspec/changes/adaptive-bitrate/tasks.md +40 -40
- package/openspec/changes/binary-distribution/.openspec.yaml +2 -2
- package/openspec/changes/binary-distribution/proposal.md +53 -53
- package/openspec/changes/chunked-request-bodies/design.md +81 -81
- package/openspec/changes/chunked-request-bodies/proposal.md +53 -53
- package/openspec/changes/chunked-request-bodies/specs/chunked-request-bodies/spec.md +30 -30
- package/openspec/changes/chunked-request-bodies/tasks.md +40 -40
- package/openspec/changes/cold-start/design.md +98 -98
- package/openspec/changes/cold-start/proposal.md +62 -62
- package/openspec/changes/cold-start/specs/cold-start/spec.md +47 -47
- package/openspec/changes/cold-start/tasks.md +48 -48
- package/openspec/changes/disk-cap/.openspec.yaml +2 -2
- package/openspec/changes/disk-cap/proposal.md +37 -37
- package/openspec/changes/disk-cap/specs/disk-cap/spec.md +25 -25
- package/openspec/changes/disk-cap/tasks.md +19 -19
- package/openspec/changes/proxy-observability/.openspec.yaml +2 -2
- package/openspec/changes/proxy-observability/design.md +38 -38
- package/openspec/changes/proxy-observability/proposal.md +49 -49
- package/openspec/changes/proxy-observability/specs/observability/spec.md +33 -33
- package/openspec/changes/proxy-observability/tasks.md +21 -21
- package/openspec/changes/seek-debounce/design.md +118 -118
- package/openspec/changes/seek-debounce/proposal.md +59 -59
- package/openspec/changes/seek-debounce/specs/seek-debounce/spec.md +41 -41
- package/openspec/changes/seek-debounce/tasks.md +33 -33
- package/openspec/changes/subtitle-language/.openspec.yaml +2 -2
- package/openspec/changes/subtitle-language/proposal.md +50 -50
- package/openspec/changes/subtitle-language/specs/subtitle-language/spec.md +34 -34
- package/openspec/changes/subtitle-language/tasks.md +26 -26
- package/openspec/changes/track-selection/.openspec.yaml +2 -2
- package/openspec/changes/track-selection/design.md +47 -47
- package/openspec/changes/track-selection/proposal.md +45 -45
- package/openspec/changes/track-selection/specs/track-selection/spec.md +41 -41
- package/openspec/changes/track-selection/tasks.md +20 -20
- package/openspec/changes/transcode-quality/.openspec.yaml +2 -2
- package/openspec/changes/transcode-quality/proposal.md +51 -51
- package/openspec/changes/transcode-quality/specs/transcode-quality/spec.md +139 -139
- package/openspec/changes/transcode-quality/tasks.md +63 -63
- package/openspec/config.yaml +38 -38
- package/package.json +50 -50
- package/public/.well-known/appspecific/com.chrome.devtools.json +1 -1
- package/research/delivery-wedge-stand-ladder-2026-09-13.md +329 -329
- package/research/double-spawn-2026-09-10.md +171 -171
- package/research/handover-2026-09-13.md +209 -209
- package/research/handover-reader-claims-removal-2026-09-12.md +166 -166
- package/research/piece-withdrawn-but-still-claimed-2026-09-12.md +175 -175
- package/research/preset-benchmark-size-scaling-2026-08-22.md +55 -55
- package/research/priority-map-is-the-truth-2026-09-12.md +225 -225
- package/routes/api/client-logs/post.js +86 -86
- package/routes/api/delivery-sink/get.js +72 -72
- package/routes/api/link-probe/get.js +51 -51
- package/routes/api/playback-plan/post.js +53 -53
- package/routes/api/sources/files/get.js +100 -100
- package/routes/api/sources/post.js +36 -36
- package/routes/api/sources/stats/get.js +132 -132
- package/routes/api/sources/warm/post.js +217 -181
- package/routes/api/subtitles/get.js +230 -230
- package/routes/api/transcode-sessions/fragment-far/post.js +60 -60
- package/routes/api/transcode-sessions/net-report/post.js +129 -129
- package/routes/api/transcode-sessions/post.js +160 -160
- package/routes/api/transcode-sessions/progress/get.js +27 -27
- package/routes/api/transcode-sessions/release/post.js +41 -41
- package/routes/api/transcode-sessions/seek/post.js +63 -63
- package/routes/health/get.js +13 -13
- package/routes/healthz/get.js +13 -13
- package/routes/stream/get.js +361 -361
- package/routes/transcode/audio-file/get.js +64 -64
- package/routes/transcode/audio-warm/get.js +84 -84
- package/routes/transcode/session-file/get.js +52 -52
- package/routes/transcode/variant-file/get.js +95 -95
- package/routes/transcode/variant-warm/get.js +84 -84
- package/scripts/render-run-graph.js +173 -173
- package/server.js +721 -720
- package/services/encode/CoverageMap.js +428 -428
- package/services/encode/CushionReport.js +345 -345
- package/services/encode/EncodeAdmission.js +332 -332
- package/services/encode/EncodeOrchestrator.js +1217 -1217
- package/services/encode/EncodePlan.js +1366 -1366
- package/services/encode/EncodeRun.js +776 -776
- package/services/encode/EncodeRuns.js +1229 -1229
- package/services/encode/Encoder.js +142 -142
- package/services/encode/EncoderSelection.js +38 -38
- package/services/encode/LocalObservations.js +359 -359
- package/services/encode/NvencEncoder.js +50 -50
- package/services/encode/OutputOpening.js +755 -755
- package/services/encode/OutputTimes.js +579 -579
- package/services/encode/QsvEncoder.js +58 -58
- package/services/encode/Renditions.js +2162 -2162
- package/services/encode/RunProgress.js +84 -84
- package/services/encode/SegmentDemand.js +95 -95
- package/services/encode/SoftwareEncoder.js +121 -121
- package/services/encode/V4l2m2mEncoder.js +53 -53
- package/services/encode/VaapiEncoder.js +66 -66
- package/services/encode/args.js +416 -416
- package/services/encode/available-share.js +89 -89
- package/services/encode/calibration.js +361 -361
- package/services/encode/contention.js +128 -128
- package/services/encode/decode-cost-fit.js +363 -363
- package/services/encode/encode-exit.js +114 -114
- package/services/encode/encode-run-state.js +551 -551
- package/services/encode/encoder-readings.js +45 -45
- package/services/encode/fingerprint.js +322 -322
- package/services/encode/host-load.js +174 -174
- package/services/encode/hwaccel.js +1894 -1894
- package/services/encode/index.js +9 -9
- package/services/encode/learned-median.js +90 -90
- package/services/encode/output/EncodedOutput.js +57 -57
- package/services/encode/output/Output.js +131 -131
- package/services/encode/output/OutputCatalog.js +414 -414
- package/services/encode/output/OutputSpec.js +390 -390
- package/services/encode/output/Timeline.js +308 -308
- package/services/encode/output/cut-grid.js +107 -107
- package/services/encode/output/index.js +20 -20
- package/services/encode/output/ladder.js +32 -32
- package/services/encode/output/playlists.js +284 -284
- package/services/encode/output/rates.js +125 -125
- package/services/encode/output-key-format.js +21 -21
- package/services/encode/quality/EncodeCost.js +1083 -1083
- package/services/encode/quality/HostLoad.js +525 -525
- package/services/encode/quality/HostTimings.js +249 -249
- package/services/encode/quality/OutputQualityState.js +28 -28
- package/services/encode/quality/QualityController.js +935 -935
- package/services/encode/quality/QualityOffer.js +416 -416
- package/services/encode/quality/drain-threat.js +47 -47
- package/services/encode/quality/link-budget.js +254 -254
- package/services/encode/quality/output-format.js +266 -266
- package/services/encode/quality/serving-output.js +216 -216
- package/services/encode/quality/visible-rung.js +57 -57
- package/services/encode/run-budget.js +97 -97
- package/services/encode/run-command.js +747 -747
- package/services/encode/run-costs.js +155 -155
- package/services/encode/run-inputs.js +42 -42
- package/services/encode/segment-formats/fmp4.js +437 -437
- package/services/encode/segment-formats/index.js +77 -77
- package/services/encode/segment-formats/init-compat.js +324 -324
- package/services/encode/segment-formats/mp4-boxes.js +311 -311
- package/services/encode/segment-formats/mpegts.js +134 -134
- package/services/encode/start-stop-cost.js +178 -178
- package/services/encode/throughput.js +97 -97
- package/services/encode/torrent-cost.js +88 -88
- package/services/identity/installation-id.js +38 -38
- package/services/media/ContainerOrchestrator.js +149 -149
- package/services/media/KeyframeTables.js +316 -316
- package/services/media/SourceFile.js +298 -298
- package/services/media/SubtitleCues.js +520 -520
- package/services/media/SubtitleOrchestrator.js +167 -167
- package/services/media/audio-inventory.js +212 -212
- package/services/media/container/AviContainer.js +266 -266
- package/services/media/container/Container.js +603 -603
- package/services/media/container/ContainerFactory.js +56 -56
- package/services/media/container/KeyframeTable.js +272 -272
- package/services/media/container/MatroskaContainer.js +1494 -1494
- package/services/media/container/Mp4Container.js +1244 -1244
- package/services/media/container/SubtitleFileContainer.js +322 -322
- package/services/media/container/ebml-reader.js +159 -159
- package/services/media/ffmpeg-banner.js +260 -260
- package/services/media/keyframe-probe.js +157 -157
- package/services/media/media-info-probe.js +93 -93
- package/services/media/playback-planner.js +856 -848
- package/services/media/tracks/AudioTrack.js +131 -131
- package/services/media/tracks/ContainerTrack.js +75 -75
- package/services/media/tracks/ImageSubtitleTrack.js +19 -19
- package/services/media/tracks/SubtitleTrack.js +38 -38
- package/services/media/tracks/TextSubtitleTrack.js +287 -287
- package/services/media/tracks/VideoTrack.js +33 -33
- package/services/media/tracks/index.js +15 -15
- package/services/media/tracks/language-detect.js +174 -174
- package/services/server/OutputLifecycle.js +418 -418
- package/services/server/SegmentServing.js +1146 -1146
- package/services/server/ViewerRequests.js +522 -522
- package/services/server/WaitLedger.js +142 -142
- package/services/server/controllers/PlaybackController.js +36 -36
- package/services/server/controllers/SubtitleController.js +109 -109
- package/services/server/http-responses.js +60 -60
- package/services/server/transcode-session-files.js +325 -325
- package/services/server/wire-outputs.js +983 -983
- package/services/storage/Diagnostics.js +202 -202
- package/services/storage/MachineBudget.js +235 -235
- package/services/storage/allowance.js +107 -107
- package/services/storage/core-dumps.js +106 -106
- package/services/storage/files/CompletedFiles.js +391 -391
- package/services/storage/files/piece-from-whole-file.js +118 -118
- package/services/storage/free.js +71 -71
- package/services/storage/keep.js +48 -48
- package/services/storage/machine-memory.js +56 -56
- package/services/storage/memory-report.js +575 -575
- package/services/storage/piece-store/piece-disk-store.js +516 -516
- package/services/storage/piece-store/piece-lru.js +495 -495
- package/services/storage/piece-store/shared-piece-store.js +1880 -1880
- package/services/storage/returns.js +103 -103
- package/services/storage/segment-store/SegmentStore.js +1196 -1196
- package/services/storage/wire.js +175 -175
- package/services/torrent/Contents.js +324 -324
- package/services/torrent/demand/DemandRegister.js +202 -202
- package/services/torrent/demand/Urgency.js +137 -137
- package/services/torrent/demand/Window.js +118 -118
- package/services/torrent/demand/index.js +8 -8
- package/services/torrent/demand/pieces.js +140 -140
- package/services/torrent/download/SwarmSelection.js +386 -386
- package/services/torrent/download/registry.js +102 -102
- package/services/torrent/download/withdraw-claim.js +80 -80
- package/services/torrent/files.js +455 -455
- package/services/torrent/naming.js +619 -619
- package/services/torrent/piece-store-of.js +29 -29
- package/services/torrent/supply-margin.js +262 -262
- package/services/torrent/torrent-pool.js +2675 -2635
- package/services/torrent/torrent-source-key.js +38 -38
- package/services/torrent/worker/background-fill.js +213 -213
- package/services/torrent/worker/channel.js +271 -271
- package/services/torrent/worker/client.js +765 -760
- package/services/torrent/worker/fastest-wires.js +342 -342
- package/services/torrent/worker/handle-state.js +23 -23
- package/services/torrent/worker/held-bytes.js +155 -155
- package/services/torrent/worker/install-webrtc-shim.js +32 -32
- package/services/torrent/worker/piece-reader.js +1078 -1078
- package/services/torrent/worker/pool-adapter.js +365 -350
- package/services/torrent/worker/protocol.js +184 -182
- package/services/torrent/worker/resume-warm.js +107 -107
- package/services/torrent/worker/webrtc-shim.js +219 -219
- package/services/torrent/worker/worker.js +1129 -1116
- package/services/transport/data-channel-handler.js +1356 -1356
- package/services/transport/delivery-probe.js +842 -842
- package/services/transport/health-collector.js +53 -53
- package/services/transport/nat-classifier.js +231 -231
- package/services/transport/packet-witness.js +808 -808
- package/services/transport/port-mapper.js +235 -235
- package/services/transport/registry-api.js +81 -81
- package/services/transport/tunnel-client.js +447 -447
- package/services/transport/usrsctp-state.js +175 -175
- package/services/transport/webrtc-manager.js +484 -484
- package/services/viewer/Assignments.js +340 -340
- package/services/viewer/PriorityMap.js +390 -390
- package/services/viewer/PriorityOrchestrator.js +325 -325
- package/services/viewer/Viewer.js +657 -657
- package/services/viewer/Viewers.js +368 -368
- package/services/viewer/active-output.js +37 -37
- package/services/viewer/audio-start.js +46 -46
- package/services/viewer/buffer-trend.js +81 -81
- package/services/viewer/choices.js +647 -647
- package/services/viewer/positions.js +126 -126
- package/services/viewer/report-intake.js +46 -46
- package/store/source-registry.js +73 -73
- package/test/asset-paths.test.js +28 -28
- package/test/audio-inventory.test.js +176 -176
- package/test/audio-track-bitrate.test.js +40 -40
- package/test/auto-quality-step.test.js +707 -707
- package/test/available-share.test.js +76 -76
- package/test/background-fill.test.js +190 -190
- package/test/client-log-file.test.js +110 -110
- package/test/completed-files.test.js +115 -115
- package/test/concurrent-cost.test.js +148 -148
- package/test/container-media-info.test.js +228 -228
- package/test/contention.test.js +76 -76
- package/test/core-dumps.test.js +57 -57
- package/test/coverage-follows-the-disk.test.js +228 -228
- package/test/coverage-map.test.js +195 -195
- package/test/cue-push-carries-what-the-walk-found.test.js +58 -58
- package/test/cut-times-timeline.test.js +65 -65
- package/test/cuts-follow-published-grid.test.js +109 -109
- package/test/data-channel-frame.test.js +63 -63
- package/test/dead-torrent-handle.test.js +52 -52
- package/test/declared-rates.test.js +116 -116
- package/test/declared-tracks.test.js +35 -35
- package/test/decode-cost-fit.test.js +202 -202
- package/test/decode-cost.test.js +462 -462
- package/test/delivery-probe.test.js +393 -393
- package/test/demand-register.test.js +206 -206
- package/test/dht-bootstrap.test.js +72 -72
- package/test/drain-threat.test.js +65 -65
- package/test/duplicate-add.test.js +48 -48
- package/test/encode-admission.test.js +146 -146
- package/test/encode-cost.test.js +283 -283
- package/test/encode-exit.test.js +111 -111
- package/test/encode-orchestrator.test.js +419 -419
- package/test/encode-plan-viewers.test.js +718 -718
- package/test/encode-plan.test.js +545 -545
- package/test/encode-run-state.test.js +314 -314
- package/test/encode-run.test.js +161 -161
- package/test/encode-slope.test.js +46 -46
- package/test/encoded-output.test.js +39 -39
- package/test/encoder-kinds.test.js +122 -122
- package/test/encoder-readings.test.js +46 -46
- package/test/every-claimant-is-told-its-share.test.js +152 -152
- package/test/failed-encoder-still-serves-what-exists.test.js +354 -354
- package/test/failed-start-breaker.test.js +202 -202
- package/test/fastest-wires.test.js +143 -143
- package/test/ffmpeg-stream-counts.test.js +78 -78
- package/test/file-edges.test.js +191 -191
- package/test/fmp4-self-contained.test.js +80 -80
- package/test/fmp4-timestamps.test.js +140 -140
- package/test/fmp4-track-completeness.test.js +41 -41
- package/test/format-keyed-outputs.test.js +78 -78
- package/test/health-metrics.test.js +38 -38
- package/test/held-request-width.test.js +157 -157
- package/test/helpers/encode-run.js +136 -136
- package/test/helpers/held-file.js +29 -29
- package/test/helpers/manager.js +57 -57
- package/test/helpers/output-spec.js +63 -63
- package/test/host-load.test.js +68 -68
- package/test/index-accuracy-is-about-the-file.test.js +128 -128
- package/test/init-compat.test.js +219 -219
- package/test/input-lost-quiets-the-plan.test.js +261 -261
- package/test/input-unavailable.test.js +48 -48
- package/test/installation-id.test.js +39 -39
- package/test/keeping-period.test.js +83 -83
- package/test/keyframe-index-accuracy.test.js +188 -188
- package/test/keyframe-table-budget.test.js +102 -102
- package/test/keyframes-belong-to-the-file.test.js +249 -249
- package/test/learned-median.test.js +37 -37
- package/test/letting-go-asks-who-is-watching.test.js +191 -191
- package/test/limit-row-by-area.test.js +60 -60
- package/test/link-budget.test.js +111 -111
- package/test/link-probe.test.js +56 -56
- package/test/local-encoding-data.test.js +273 -273
- package/test/logger-repeats.test.js +134 -134
- package/test/look-ahead-coverage.test.js +56 -56
- package/test/manager-surface.test.js +292 -292
- package/test/matroska-cues-track.test.js +192 -192
- package/test/memory-budget.test.js +390 -390
- package/test/move-cost.test.js +216 -216
- package/test/mp4-subtitles.test.js +173 -173
- package/test/one-authority.test.js +285 -285
- package/test/one-budget.test.js +149 -149
- package/test/one-output-one-session.test.js +204 -204
- package/test/one-resource-divided.test.js +185 -185
- package/test/one-serving-output-per-height.test.js +458 -458
- package/test/orchestrator-wired.test.js +206 -206
- package/test/output-catalog.test.js +39 -39
- package/test/output-shape.test.js +21 -21
- package/test/output-spec.test.js +251 -251
- package/test/output-speed.test.js +88 -88
- package/test/outputs-being-prepared.test.js +40 -40
- package/test/packet-witness-ring.test.js +236 -236
- package/test/packet-witness.test.js +148 -148
- package/test/piece-disk-store.test.js +381 -381
- package/test/piece-from-whole-file.test.js +129 -129
- package/test/piece-lru.test.js +319 -319
- package/test/piece-reader.test.js +162 -162
- package/test/piece-selection-offset.test.js +85 -85
- package/test/piece-store-eviction.test.js +560 -560
- package/test/piece-store-never-refuses.test.js +153 -153
- package/test/piece-store-reservations.test.js +318 -318
- package/test/piece-store-slow-disk.test.js +159 -159
- package/test/piece-tail.test.js +97 -97
- package/test/plan-host-timings.test.js +68 -68
- package/test/plan-is-steady.test.js +225 -225
- package/test/priority-map-download.test.js +174 -174
- package/test/priority-map-emptied.test.js +204 -204
- package/test/priority-map-per-output.test.js +215 -215
- package/test/priority-map.test.js +163 -163
- package/test/probe-wedge-certainty.test.js +60 -60
- package/test/produced-copy-choice.test.js +213 -213
- package/test/published-timeline.test.js +83 -83
- package/test/quality-offer.test.js +162 -162
- package/test/quality-variants.test.js +1229 -1229
- package/test/rate-control-in-the-output.test.js +180 -180
- package/test/read-survives-withdrawal.test.js +164 -164
- package/test/read-window.test.js +365 -365
- package/test/resume-warm.test.js +39 -39
- package/test/run-graph-drift.test.js +37 -37
- package/test/run-inputs.test.js +80 -80
- package/test/run-intervals.test.js +100 -100
- package/test/run-position-follows-published-grid.test.js +89 -89
- package/test/run-progress.test.js +65 -65
- package/test/same-height-switch.test.js +664 -664
- package/test/seek-frees-the-viewers-requests.test.js +217 -217
- package/test/seek-landing.test.js +109 -109
- package/test/seek-target-not-superseded.test.js +195 -195
- package/test/segment-closed-on-both-branches.test.js +96 -96
- package/test/segment-cut-times.test.js +63 -63
- package/test/segment-demand.test.js +80 -80
- package/test/segment-hold-supersede.test.js +91 -91
- package/test/segment-judged-by-its-own-header.test.js +221 -221
- package/test/segment-serve-wiring.test.js +475 -475
- package/test/segment-store-eviction.test.js +247 -247
- package/test/segment-store.test.js +323 -323
- package/test/segment-true-start.test.js +86 -86
- package/test/segments-are-shared.test.js +176 -176
- package/test/send-chunk-bytes.test.js +86 -86
- package/test/serving-existing-output.test.js +102 -102
- package/test/serving-output.test.js +102 -102
- package/test/shared-piece-store.test.js +310 -310
- package/test/sidecar-naming.test.js +142 -142
- package/test/source-file.test.js +133 -133
- package/test/source-files-route.test.js +103 -103
- package/test/split-a-stretch.test.js +139 -139
- package/test/stale-request-after-seek.test.js +158 -158
- package/test/startup-readings.test.js +124 -124
- package/test/stream-route.test.js +182 -182
- package/test/subtitle-cue-framing.test.js +200 -200
- package/test/subtitle-cue-source.test.js +102 -102
- package/test/subtitle-cue-walk.test.js +370 -370
- package/test/subtitle-cursor.test.js +63 -63
- package/test/subtitle-defaults.test.js +118 -118
- package/test/subtitle-language.test.js +252 -252
- package/test/subtitle-track-numbering.test.js +386 -386
- package/test/supply-margin.test.js +177 -177
- package/test/swarm-follows-readers.test.js +174 -174
- package/test/swarm-reach.test.js +130 -130
- package/test/swarm-selection.test.js +220 -220
- package/test/tail-duplication.test.js +167 -167
- package/test/the-channel-has-both-ends.test.js +91 -91
- package/test/the-torrent-thread-serves-bytes.test.js +107 -107
- package/test/thread-pool.test.js +53 -53
- package/test/timeline.test.js +84 -84
- package/test/torrent-contents.test.js +235 -235
- package/test/torrent-cost.test.js +71 -71
- package/test/torrent-files.test.js +221 -221
- package/test/torrent-naming.test.js +255 -255
- package/test/tracks-begin-together.test.js +209 -209
- package/test/tunnel-renewal.test.js +167 -167
- package/test/two-viewers-one-picture.test.js +407 -407
- package/test/upload-hurry.test.js +186 -186
- package/test/usrsctp-state.test.js +131 -131
- package/test/video-facts.test.js +102 -102
- package/test/viewer-assignments.test.js +143 -143
- package/test/viewer-outputs.test.js +285 -285
- package/test/viewer-presence.test.js +116 -116
- package/test/viewer-requests-admission.test.js +89 -89
- package/test/viewer-states.test.js +189 -189
- package/test/viewer-subtitle-subscription.test.js +96 -96
- package/test/viewer.test.js +139 -139
- package/test/viewing-generations.test.js +390 -390
- package/test/visible-rung.test.js +37 -37
- package/test/wait-ledger.test.js +100 -100
- package/test/webrtc-shim.test.js +149 -149
- package/test/wedge-certainty.test.js +131 -131
- package/test/withdraw-piece-claim.test.js +212 -212
- package/test/worker-channel.test.js +158 -158
- package/utils/client-log-file.js +151 -151
- package/utils/logger.js +312 -312
- package/utils/parse-range.js +25 -25
- package/utils/perf.js +59 -59
|
@@ -1,159 +1,159 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file Minimal EBML reader — the encoding Matroska/WebM are built from.
|
|
3
|
-
*
|
|
4
|
-
* Only what a keyframe index needs: walk elements, read unsigned integers,
|
|
5
|
-
* descend into containers. Deliberately not a general EBML implementation.
|
|
6
|
-
*
|
|
7
|
-
* Why not the `ebml` npm package (MIT, otherwise fine): its decoder is a
|
|
8
|
-
* *stream* decoder — it must be fed the file from byte zero. We read two small
|
|
9
|
-
* ranges out of a multi-gigabyte torrent-backed file and never the whole thing,
|
|
10
|
-
* so a parser that starts mid-file is exactly what we need and exactly what it
|
|
11
|
-
* cannot do (verified 2026-08-02: feeding it the Cues range throws
|
|
12
|
-
* "Unrepresentable length"). The element encoding itself is small enough to
|
|
13
|
-
* implement directly, so we do — the shape follows the Matroska specification
|
|
14
|
-
* and the same logic those libraries implement.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* A variable-length integer, as EBML encodes both element ids and sizes.
|
|
19
|
-
*
|
|
20
|
-
* The first set bit of the leading byte marks the width: `1xxxxxxx` is one
|
|
21
|
-
* byte, `01xxxxxx` two, and so on up to eight. For a SIZE the marker bit is
|
|
22
|
-
* removed and the remainder is the value; for an ID the bytes are kept intact,
|
|
23
|
-
* because the id *is* those bytes (that is how `0x1C53BB6B` identifies Cues).
|
|
24
|
-
*
|
|
25
|
-
* @param {Buffer} buffer
|
|
26
|
-
* @param {number} offset
|
|
27
|
-
* @param {boolean} keepMarker - True for ids, false for sizes.
|
|
28
|
-
* @returns {{ value: number, length: number } | null} Null when truncated or malformed.
|
|
29
|
-
*/
|
|
30
|
-
export function readVint(buffer, offset, keepMarker) {
|
|
31
|
-
if (offset >= buffer.length) {
|
|
32
|
-
return null;
|
|
33
|
-
}
|
|
34
|
-
const first = buffer[offset];
|
|
35
|
-
if (first === 0) {
|
|
36
|
-
return null; // No marker bit in the first byte: not a valid vint start.
|
|
37
|
-
}
|
|
38
|
-
let length = 1;
|
|
39
|
-
let mask = 0x80;
|
|
40
|
-
while (length <= 8 && (first & mask) === 0) {
|
|
41
|
-
length += 1;
|
|
42
|
-
mask >>= 1;
|
|
43
|
-
}
|
|
44
|
-
if (length > 8 || offset + length > buffer.length) {
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
47
|
-
let value = keepMarker ? first : first & (mask - 1);
|
|
48
|
-
for (let index = 1; index < length; index += 1) {
|
|
49
|
-
// Values beyond 2^53 cannot be represented exactly; sizes and positions in
|
|
50
|
-
// real files stay far below that, so plain arithmetic is safe here.
|
|
51
|
-
value = value * 256 + buffer[offset + index];
|
|
52
|
-
}
|
|
53
|
-
return { value, length };
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Iterate the elements directly inside `buffer`, without descending.
|
|
58
|
-
*
|
|
59
|
-
* Yields each element's id, the offset of its payload and its size. An element
|
|
60
|
-
* whose payload runs past the end of the buffer is still yielded (its header is
|
|
61
|
-
* intact and the caller may only need the id), but iteration stops after it.
|
|
62
|
-
*
|
|
63
|
-
* @param {Buffer} buffer
|
|
64
|
-
* @param {number} [start=0]
|
|
65
|
-
* @param {number} [end=buffer.length]
|
|
66
|
-
* @yields {{ id: number, dataOffset: number, size: number }}
|
|
67
|
-
*/
|
|
68
|
-
export function* iterateElements(buffer, start = 0, end = buffer.length) {
|
|
69
|
-
let offset = start;
|
|
70
|
-
while (offset < end) {
|
|
71
|
-
const id = readVint(buffer, offset, true);
|
|
72
|
-
if (!id) {
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
const size = readVint(buffer, offset + id.length, false);
|
|
76
|
-
if (!size) {
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
const dataOffset = offset + id.length + size.length;
|
|
80
|
-
yield { id: id.value, dataOffset, size: size.value };
|
|
81
|
-
if (dataOffset + size.value > end) {
|
|
82
|
-
return; // Truncated payload — nothing dependable follows it.
|
|
83
|
-
}
|
|
84
|
-
offset = dataOffset + size.value;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Read an EBML unsigned integer payload (big-endian, variable width).
|
|
90
|
-
*
|
|
91
|
-
* @param {Buffer} buffer
|
|
92
|
-
* @param {number} offset
|
|
93
|
-
* @param {number} size
|
|
94
|
-
* @returns {number}
|
|
95
|
-
*/
|
|
96
|
-
export function readUint(buffer, offset, size) {
|
|
97
|
-
let value = 0;
|
|
98
|
-
for (let index = 0; index < size; index += 1) {
|
|
99
|
-
value = value * 256 + buffer[offset + index];
|
|
100
|
-
}
|
|
101
|
-
return value;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* An EBML float, which RFC 9559 §11.3.3 allows to be 4 or 8 bytes, big-endian
|
|
106
|
-
* IEEE 754, and 0 bytes for the value zero.
|
|
107
|
-
*
|
|
108
|
-
* Needed because Matroska writes `Duration` as a float, not as an integer:
|
|
109
|
-
* reading it with {@link readUint} yields the bit pattern rather than the
|
|
110
|
-
* number. Any other size is malformed and answered with null rather than a
|
|
111
|
-
* guess.
|
|
112
|
-
*
|
|
113
|
-
* @param {Buffer} buffer
|
|
114
|
-
* @param {number} offset
|
|
115
|
-
* @param {number} size
|
|
116
|
-
* @returns {number | null}
|
|
117
|
-
*/
|
|
118
|
-
export function readFloat(buffer, offset, size) {
|
|
119
|
-
if (size === 0) {
|
|
120
|
-
return 0;
|
|
121
|
-
}
|
|
122
|
-
if (offset + size > buffer.length) {
|
|
123
|
-
return null;
|
|
124
|
-
}
|
|
125
|
-
if (size === 4) {
|
|
126
|
-
return buffer.readFloatBE(offset);
|
|
127
|
-
}
|
|
128
|
-
if (size === 8) {
|
|
129
|
-
return buffer.readDoubleBE(offset);
|
|
130
|
-
}
|
|
131
|
-
return null;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Depth-first search for the first element with `id`, descending only into the
|
|
136
|
-
* container ids listed in `descendInto`.
|
|
137
|
-
*
|
|
138
|
-
* @param {Buffer} buffer
|
|
139
|
-
* @param {number} id - Element id to find.
|
|
140
|
-
* @param {number[]} descendInto - Container ids worth entering.
|
|
141
|
-
* @param {number} [start=0]
|
|
142
|
-
* @param {number} [end=buffer.length]
|
|
143
|
-
* @returns {{ dataOffset: number, size: number } | null}
|
|
144
|
-
*/
|
|
145
|
-
export function findElement(buffer, id, descendInto, start = 0, end = buffer.length) {
|
|
146
|
-
for (const element of iterateElements(buffer, start, end)) {
|
|
147
|
-
if (element.id === id) {
|
|
148
|
-
return { dataOffset: element.dataOffset, size: element.size };
|
|
149
|
-
}
|
|
150
|
-
if (descendInto.includes(element.id)) {
|
|
151
|
-
const limit = Math.min(end, element.dataOffset + element.size);
|
|
152
|
-
const found = findElement(buffer, id, descendInto, element.dataOffset, limit);
|
|
153
|
-
if (found) {
|
|
154
|
-
return found;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
return null;
|
|
159
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* @file Minimal EBML reader — the encoding Matroska/WebM are built from.
|
|
3
|
+
*
|
|
4
|
+
* Only what a keyframe index needs: walk elements, read unsigned integers,
|
|
5
|
+
* descend into containers. Deliberately not a general EBML implementation.
|
|
6
|
+
*
|
|
7
|
+
* Why not the `ebml` npm package (MIT, otherwise fine): its decoder is a
|
|
8
|
+
* *stream* decoder — it must be fed the file from byte zero. We read two small
|
|
9
|
+
* ranges out of a multi-gigabyte torrent-backed file and never the whole thing,
|
|
10
|
+
* so a parser that starts mid-file is exactly what we need and exactly what it
|
|
11
|
+
* cannot do (verified 2026-08-02: feeding it the Cues range throws
|
|
12
|
+
* "Unrepresentable length"). The element encoding itself is small enough to
|
|
13
|
+
* implement directly, so we do — the shape follows the Matroska specification
|
|
14
|
+
* and the same logic those libraries implement.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A variable-length integer, as EBML encodes both element ids and sizes.
|
|
19
|
+
*
|
|
20
|
+
* The first set bit of the leading byte marks the width: `1xxxxxxx` is one
|
|
21
|
+
* byte, `01xxxxxx` two, and so on up to eight. For a SIZE the marker bit is
|
|
22
|
+
* removed and the remainder is the value; for an ID the bytes are kept intact,
|
|
23
|
+
* because the id *is* those bytes (that is how `0x1C53BB6B` identifies Cues).
|
|
24
|
+
*
|
|
25
|
+
* @param {Buffer} buffer
|
|
26
|
+
* @param {number} offset
|
|
27
|
+
* @param {boolean} keepMarker - True for ids, false for sizes.
|
|
28
|
+
* @returns {{ value: number, length: number } | null} Null when truncated or malformed.
|
|
29
|
+
*/
|
|
30
|
+
export function readVint(buffer, offset, keepMarker) {
|
|
31
|
+
if (offset >= buffer.length) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
const first = buffer[offset];
|
|
35
|
+
if (first === 0) {
|
|
36
|
+
return null; // No marker bit in the first byte: not a valid vint start.
|
|
37
|
+
}
|
|
38
|
+
let length = 1;
|
|
39
|
+
let mask = 0x80;
|
|
40
|
+
while (length <= 8 && (first & mask) === 0) {
|
|
41
|
+
length += 1;
|
|
42
|
+
mask >>= 1;
|
|
43
|
+
}
|
|
44
|
+
if (length > 8 || offset + length > buffer.length) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
let value = keepMarker ? first : first & (mask - 1);
|
|
48
|
+
for (let index = 1; index < length; index += 1) {
|
|
49
|
+
// Values beyond 2^53 cannot be represented exactly; sizes and positions in
|
|
50
|
+
// real files stay far below that, so plain arithmetic is safe here.
|
|
51
|
+
value = value * 256 + buffer[offset + index];
|
|
52
|
+
}
|
|
53
|
+
return { value, length };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Iterate the elements directly inside `buffer`, without descending.
|
|
58
|
+
*
|
|
59
|
+
* Yields each element's id, the offset of its payload and its size. An element
|
|
60
|
+
* whose payload runs past the end of the buffer is still yielded (its header is
|
|
61
|
+
* intact and the caller may only need the id), but iteration stops after it.
|
|
62
|
+
*
|
|
63
|
+
* @param {Buffer} buffer
|
|
64
|
+
* @param {number} [start=0]
|
|
65
|
+
* @param {number} [end=buffer.length]
|
|
66
|
+
* @yields {{ id: number, dataOffset: number, size: number }}
|
|
67
|
+
*/
|
|
68
|
+
export function* iterateElements(buffer, start = 0, end = buffer.length) {
|
|
69
|
+
let offset = start;
|
|
70
|
+
while (offset < end) {
|
|
71
|
+
const id = readVint(buffer, offset, true);
|
|
72
|
+
if (!id) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const size = readVint(buffer, offset + id.length, false);
|
|
76
|
+
if (!size) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const dataOffset = offset + id.length + size.length;
|
|
80
|
+
yield { id: id.value, dataOffset, size: size.value };
|
|
81
|
+
if (dataOffset + size.value > end) {
|
|
82
|
+
return; // Truncated payload — nothing dependable follows it.
|
|
83
|
+
}
|
|
84
|
+
offset = dataOffset + size.value;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Read an EBML unsigned integer payload (big-endian, variable width).
|
|
90
|
+
*
|
|
91
|
+
* @param {Buffer} buffer
|
|
92
|
+
* @param {number} offset
|
|
93
|
+
* @param {number} size
|
|
94
|
+
* @returns {number}
|
|
95
|
+
*/
|
|
96
|
+
export function readUint(buffer, offset, size) {
|
|
97
|
+
let value = 0;
|
|
98
|
+
for (let index = 0; index < size; index += 1) {
|
|
99
|
+
value = value * 256 + buffer[offset + index];
|
|
100
|
+
}
|
|
101
|
+
return value;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* An EBML float, which RFC 9559 §11.3.3 allows to be 4 or 8 bytes, big-endian
|
|
106
|
+
* IEEE 754, and 0 bytes for the value zero.
|
|
107
|
+
*
|
|
108
|
+
* Needed because Matroska writes `Duration` as a float, not as an integer:
|
|
109
|
+
* reading it with {@link readUint} yields the bit pattern rather than the
|
|
110
|
+
* number. Any other size is malformed and answered with null rather than a
|
|
111
|
+
* guess.
|
|
112
|
+
*
|
|
113
|
+
* @param {Buffer} buffer
|
|
114
|
+
* @param {number} offset
|
|
115
|
+
* @param {number} size
|
|
116
|
+
* @returns {number | null}
|
|
117
|
+
*/
|
|
118
|
+
export function readFloat(buffer, offset, size) {
|
|
119
|
+
if (size === 0) {
|
|
120
|
+
return 0;
|
|
121
|
+
}
|
|
122
|
+
if (offset + size > buffer.length) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
if (size === 4) {
|
|
126
|
+
return buffer.readFloatBE(offset);
|
|
127
|
+
}
|
|
128
|
+
if (size === 8) {
|
|
129
|
+
return buffer.readDoubleBE(offset);
|
|
130
|
+
}
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Depth-first search for the first element with `id`, descending only into the
|
|
136
|
+
* container ids listed in `descendInto`.
|
|
137
|
+
*
|
|
138
|
+
* @param {Buffer} buffer
|
|
139
|
+
* @param {number} id - Element id to find.
|
|
140
|
+
* @param {number[]} descendInto - Container ids worth entering.
|
|
141
|
+
* @param {number} [start=0]
|
|
142
|
+
* @param {number} [end=buffer.length]
|
|
143
|
+
* @returns {{ dataOffset: number, size: number } | null}
|
|
144
|
+
*/
|
|
145
|
+
export function findElement(buffer, id, descendInto, start = 0, end = buffer.length) {
|
|
146
|
+
for (const element of iterateElements(buffer, start, end)) {
|
|
147
|
+
if (element.id === id) {
|
|
148
|
+
return { dataOffset: element.dataOffset, size: element.size };
|
|
149
|
+
}
|
|
150
|
+
if (descendInto.includes(element.id)) {
|
|
151
|
+
const limit = Math.min(end, element.dataOffset + element.size);
|
|
152
|
+
const found = findElement(buffer, id, descendInto, element.dataOffset, limit);
|
|
153
|
+
if (found) {
|
|
154
|
+
return found;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return null;
|
|
159
|
+
}
|