@torrent-tv/proxy 2.89.7 → 2.89.9
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 +363 -338
- package/CHANGELOG.md +2345 -2335
- package/Dockerfile +30 -30
- package/LICENSE +16 -16
- package/README.md +369 -369
- package/assets/calibration/NOTICE.md +100 -100
- package/assets/calibration/audio/NOTICE.md +19 -19
- package/assets/calibration/audio/manifest.json +98 -98
- package/assets/diagnostics/sctpstate.gdb +31 -31
- package/bin/cli.js +684 -684
- package/bin/thread-pool.js +30 -30
- package/biome.json +412 -412
- package/docs/browser-proxy-contract.md +155 -155
- package/docs/container-architecture.md +273 -273
- package/docs/download-architecture.md +261 -261
- package/docs/encode-architecture.md +765 -765
- package/docs/encode-run-state.md +96 -96
- package/docs/logs.md +97 -97
- package/docs/playback-start-forecast.md +113 -113
- package/docs/socks5-egress-research.md +114 -114
- package/docs/storage-architecture.md +273 -273
- 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 +51 -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 -106
- 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 -178
- 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 +416 -416
- package/routes/transcode/audio-file/get.js +64 -64
- package/routes/transcode/audio-warm/get.js +95 -95
- 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/build-audio-samples.mjs +31 -31
- package/scripts/render-run-graph.js +173 -173
- package/server.js +748 -748
- 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 +1192 -1192
- package/services/encode/EncodePlan.js +1368 -1368
- package/services/encode/EncodeRun.js +36 -12
- 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 +784 -784
- package/services/encode/OutputTimes.js +579 -579
- package/services/encode/QsvEncoder.js +58 -58
- package/services/encode/Renditions.js +2283 -2283
- package/services/encode/RunClock.js +87 -87
- package/services/encode/RunProgress.js +79 -79
- 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 -418
- package/services/encode/audio-calibration.js +67 -67
- package/services/encode/audio-work-rate.js +15 -15
- 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 +116 -116
- package/services/encode/encode-run-state.js +551 -551
- package/services/encode/encoder-readings.js +43 -43
- 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 +24 -24
- 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 +1043 -1043
- package/services/encode/quality/HostLoad.js +525 -525
- package/services/encode/quality/HostTimings.js +249 -249
- package/services/encode/quality/OutputQualityState.js +24 -24
- package/services/encode/quality/QualityController.js +758 -758
- package/services/encode/quality/QualityOffer.js +416 -416
- package/services/encode/quality/drain-threat.js +47 -47
- package/services/encode/quality/link-budget.js +337 -337
- package/services/encode/quality/output-format.js +319 -319
- 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 +757 -757
- package/services/encode/run-costs.js +155 -155
- package/services/encode/run-inputs.js +53 -53
- package/services/encode/segment-formats/fmp4.js +476 -476
- package/services/encode/segment-formats/init-compat.js +324 -324
- 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 +202 -202
- package/services/media/KeyframeTables.js +356 -356
- package/services/media/SourceFile.js +298 -298
- package/services/media/SubtitleCues.js +592 -592
- package/services/media/SubtitleOrchestrator.js +167 -167
- package/services/media/audio-inventory.js +299 -299
- package/services/media/container/AviContainer.js +268 -268
- package/services/media/container/Container.js +632 -632
- package/services/media/container/ContainerFactory.js +38 -38
- package/services/media/container/KeyframeTable.js +272 -272
- package/services/media/container/MatroskaContainer.js +1031 -1031
- package/services/media/container/Mp4Container.js +1354 -1354
- package/services/media/container/SubtitleFileContainer.js +339 -339
- package/services/media/container/ebml-reader.js +159 -159
- package/services/media/container/ebml-stream.js +174 -174
- package/services/media/container/matroska-clusters.js +1165 -1165
- package/services/media/container/unavailable.js +77 -77
- 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 -143
- package/services/media/tracks/ContainerTrack.js +84 -84
- package/services/media/tracks/ImageSubtitleTrack.js +19 -19
- package/services/media/tracks/SubtitleTrack.js +38 -38
- package/services/media/tracks/TextSubtitleTrack.js +295 -295
- package/services/media/tracks/VideoTrack.js +33 -33
- package/services/media/tracks/audio-bound.js +233 -233
- 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 +1152 -1152
- package/services/server/ViewerRequests.js +785 -785
- 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/transcode-session-files.js +325 -325
- package/services/server/wire-outputs.js +1029 -1029
- 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 +1937 -1937
- package/services/storage/returns.js +103 -103
- package/services/storage/segment-store/SegmentStore.js +1294 -1294
- package/services/storage/wire.js +175 -175
- package/services/torrent/Contents.js +382 -382
- 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 +264 -264
- package/services/torrent/torrent-pool.js +2688 -2688
- package/services/torrent/worker/background-fill.js +213 -213
- package/services/torrent/worker/channel.js +271 -271
- package/services/torrent/worker/client.js +836 -836
- 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 +231 -231
- 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 +1137 -1137
- package/services/transport/data-channel-handler.js +1406 -1373
- package/services/transport/delivery-probe.js +798 -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 +421 -421
- 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/media-time.js +355 -355
- package/services/viewer/playback-readiness.js +635 -635
- 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 +77 -77
- package/test/audio-calibration.test.js +23 -23
- package/test/audio-inventory.test.js +212 -212
- package/test/audio-track-bitrate.test.js +40 -40
- package/test/auto-quality-step.test.js +762 -762
- 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/coalesce.test.js +39 -39
- 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 +233 -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 +162 -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 +475 -475
- package/test/delivery-probe-field.test.js +173 -0
- package/test/delivery-probe.test.js +382 -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 +275 -275
- package/test/encode-exit.test.js +111 -111
- package/test/encode-orchestrator.test.js +426 -426
- package/test/encode-plan-viewers.test.js +725 -725
- package/test/encode-plan.test.js +545 -545
- package/test/encode-run-state.test.js +314 -314
- package/test/encode-run.test.js +20 -0
- 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 +68 -68
- 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 -1
- 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 +36 -38
- package/test/held-read-ownership.test.js +77 -77
- package/test/held-request-width.test.js +157 -157
- package/test/helpers/encode-run.js +248 -240
- package/test/helpers/held-file.js +77 -77
- package/test/helpers/manager.js +62 -62
- package/test/helpers/matroska-file.js +296 -296
- 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 +123 -123
- 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 +291 -291
- package/test/matroska-cluster-discovery.test.js +323 -323
- package/test/matroska-cues-track.test.js +192 -192
- package/test/media-time.test.js +62 -62
- 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 +273 -273
- 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 +207 -207
- 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 +92 -87
- 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 +518 -518
- package/test/playback-start-regressions.test.js +101 -101
- 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 +160 -160
- package/test/quality-variants.test.js +1274 -1270
- package/test/rate-control-in-the-output.test.js +181 -180
- package/test/read-survives-withdrawal.test.js +164 -164
- package/test/read-window.test.js +365 -365
- package/test/reads-not-yet-arrived.test.js +218 -218
- 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 +64 -64
- package/test/same-height-switch.test.js +663 -663
- 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 +114 -114
- 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 +222 -222
- package/test/segment-serve-wiring.test.js +481 -481
- package/test/segment-store-eviction.test.js +247 -247
- package/test/segment-store.test.js +352 -352
- 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 +111 -111
- 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 -289
- 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 +237 -237
- 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 +385 -385
- 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 -15
- package/test/subtitle-plan-waits.test.js +125 -125
- package/test/subtitle-track-numbering.test.js +391 -391
- package/test/supply-margin.test.js +190 -190
- 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 +508 -508
- 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 +92 -92
- package/test/viewer-starts-on-map.test.js +27 -27
- package/test/viewer-states.test.js +192 -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/whole-file-held-read.test.js +30 -30
- 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/coalesce.js +41 -41
- 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
package/utils/parse-range.js
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file HTTP Range header parser.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Parse an HTTP `Range` header value into a byte range clamped to the file size.
|
|
7
|
-
* Only the `bytes=<start>-<end>` form is supported.
|
|
8
|
-
*
|
|
9
|
-
* @param {string | undefined} rangeHeader - Value of the `Range` request header.
|
|
10
|
-
* @param {number} fileLength - Total file size in bytes.
|
|
11
|
-
* @returns {{ start: number, end: number } | null} Byte range, or `null` if the
|
|
12
|
-
* header is absent, malformed, or uses an unsupported unit.
|
|
13
|
-
*/
|
|
14
|
-
export function parseRange(rangeHeader, fileLength) {
|
|
15
|
-
if (!rangeHeader || !rangeHeader.startsWith("bytes=")) {
|
|
16
|
-
return null;
|
|
17
|
-
}
|
|
18
|
-
const [startRaw, endRaw] = rangeHeader.slice("bytes=".length).split("-");
|
|
19
|
-
const start = startRaw.length > 0 ? Number(startRaw) : 0;
|
|
20
|
-
const end = endRaw && endRaw.length > 0 ? Number(endRaw) : fileLength - 1;
|
|
21
|
-
if (!Number.isInteger(start) || !Number.isInteger(end) || start < 0 || end < start) {
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
return { start, end: Math.min(end, fileLength - 1) };
|
|
25
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* @file HTTP Range header parser.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Parse an HTTP `Range` header value into a byte range clamped to the file size.
|
|
7
|
+
* Only the `bytes=<start>-<end>` form is supported.
|
|
8
|
+
*
|
|
9
|
+
* @param {string | undefined} rangeHeader - Value of the `Range` request header.
|
|
10
|
+
* @param {number} fileLength - Total file size in bytes.
|
|
11
|
+
* @returns {{ start: number, end: number } | null} Byte range, or `null` if the
|
|
12
|
+
* header is absent, malformed, or uses an unsupported unit.
|
|
13
|
+
*/
|
|
14
|
+
export function parseRange(rangeHeader, fileLength) {
|
|
15
|
+
if (!rangeHeader || !rangeHeader.startsWith("bytes=")) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
const [startRaw, endRaw] = rangeHeader.slice("bytes=".length).split("-");
|
|
19
|
+
const start = startRaw.length > 0 ? Number(startRaw) : 0;
|
|
20
|
+
const end = endRaw && endRaw.length > 0 ? Number(endRaw) : fileLength - 1;
|
|
21
|
+
if (!Number.isInteger(start) || !Number.isInteger(end) || start < 0 || end < start) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
return { start, end: Math.min(end, fileLength - 1) };
|
|
25
|
+
}
|
package/utils/perf.js
CHANGED
|
@@ -1,59 +1,59 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file Runtime performance instrumentation.
|
|
3
|
-
*
|
|
4
|
-
* Exists because a field seek took minutes and every explanation offered for it
|
|
5
|
-
* — encoder too slow, torrent too slow, channel too slow, event loop starved —
|
|
6
|
-
* was a guess. The numbers that would have settled it were not being recorded.
|
|
7
|
-
* This records them.
|
|
8
|
-
*
|
|
9
|
-
* Two things are measured:
|
|
10
|
-
*
|
|
11
|
-
* - **Event loop delay** (`perf_hooks.monitorEventLoopDelay`). If synchronous
|
|
12
|
-
* work (torrent piece hashing, large buffer handling) blocks the loop, every
|
|
13
|
-
* read and every send waits behind it, and the symptom looks exactly like a
|
|
14
|
-
* slow network. The histogram distinguishes the two beyond argument.
|
|
15
|
-
* - **Named operation timings**, so a slow transfer can be attributed to the
|
|
16
|
-
* step that actually consumed the time rather than to the whole.
|
|
17
|
-
*
|
|
18
|
-
* Deliberately cheap: a histogram sampled every 20 ms, and plain arithmetic per
|
|
19
|
-
* operation. No trace files, no profiler — `--trace-events-enabled` or
|
|
20
|
-
* `--cpu-prof` remain available for a deeper look when these figures point
|
|
21
|
-
* somewhere specific.
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
import { monitorEventLoopDelay } from "node:perf_hooks";
|
|
25
|
-
|
|
26
|
-
const NANOSECONDS_PER_MILLISECOND = 1e6;
|
|
27
|
-
// Sampling interval for the loop-delay histogram. 20 ms is fine enough to catch
|
|
28
|
-
// the stalls that matter (tens of ms and up) without measurable overhead.
|
|
29
|
-
const LOOP_SAMPLE_INTERVAL_MS = 20;
|
|
30
|
-
|
|
31
|
-
const loopDelay = monitorEventLoopDelay({ resolution: LOOP_SAMPLE_INTERVAL_MS });
|
|
32
|
-
loopDelay.enable();
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Event-loop delay since the last {@link resetEventLoopDelay}, in milliseconds.
|
|
36
|
-
*
|
|
37
|
-
* `mean` is the everyday cost; `max` and `p99` are what a single blocking spell
|
|
38
|
-
* does to whatever was waiting. A transfer that looks network-bound but shows a
|
|
39
|
-
* large `max` here was not network-bound at all.
|
|
40
|
-
*
|
|
41
|
-
* @returns {{ meanMs: number, p99Ms: number, maxMs: number }}
|
|
42
|
-
*/
|
|
43
|
-
export function eventLoopDelay() {
|
|
44
|
-
return {
|
|
45
|
-
meanMs: loopDelay.mean / NANOSECONDS_PER_MILLISECOND,
|
|
46
|
-
p99Ms: loopDelay.percentile(99) / NANOSECONDS_PER_MILLISECOND,
|
|
47
|
-
maxMs: loopDelay.max / NANOSECONDS_PER_MILLISECOND
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Start a fresh measurement window for the loop-delay histogram, so a reported
|
|
53
|
-
* figure describes one operation rather than the process's whole lifetime.
|
|
54
|
-
*
|
|
55
|
-
* @returns {void}
|
|
56
|
-
*/
|
|
57
|
-
export function resetEventLoopDelay() {
|
|
58
|
-
loopDelay.reset();
|
|
59
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* @file Runtime performance instrumentation.
|
|
3
|
+
*
|
|
4
|
+
* Exists because a field seek took minutes and every explanation offered for it
|
|
5
|
+
* — encoder too slow, torrent too slow, channel too slow, event loop starved —
|
|
6
|
+
* was a guess. The numbers that would have settled it were not being recorded.
|
|
7
|
+
* This records them.
|
|
8
|
+
*
|
|
9
|
+
* Two things are measured:
|
|
10
|
+
*
|
|
11
|
+
* - **Event loop delay** (`perf_hooks.monitorEventLoopDelay`). If synchronous
|
|
12
|
+
* work (torrent piece hashing, large buffer handling) blocks the loop, every
|
|
13
|
+
* read and every send waits behind it, and the symptom looks exactly like a
|
|
14
|
+
* slow network. The histogram distinguishes the two beyond argument.
|
|
15
|
+
* - **Named operation timings**, so a slow transfer can be attributed to the
|
|
16
|
+
* step that actually consumed the time rather than to the whole.
|
|
17
|
+
*
|
|
18
|
+
* Deliberately cheap: a histogram sampled every 20 ms, and plain arithmetic per
|
|
19
|
+
* operation. No trace files, no profiler — `--trace-events-enabled` or
|
|
20
|
+
* `--cpu-prof` remain available for a deeper look when these figures point
|
|
21
|
+
* somewhere specific.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { monitorEventLoopDelay } from "node:perf_hooks";
|
|
25
|
+
|
|
26
|
+
const NANOSECONDS_PER_MILLISECOND = 1e6;
|
|
27
|
+
// Sampling interval for the loop-delay histogram. 20 ms is fine enough to catch
|
|
28
|
+
// the stalls that matter (tens of ms and up) without measurable overhead.
|
|
29
|
+
const LOOP_SAMPLE_INTERVAL_MS = 20;
|
|
30
|
+
|
|
31
|
+
const loopDelay = monitorEventLoopDelay({ resolution: LOOP_SAMPLE_INTERVAL_MS });
|
|
32
|
+
loopDelay.enable();
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Event-loop delay since the last {@link resetEventLoopDelay}, in milliseconds.
|
|
36
|
+
*
|
|
37
|
+
* `mean` is the everyday cost; `max` and `p99` are what a single blocking spell
|
|
38
|
+
* does to whatever was waiting. A transfer that looks network-bound but shows a
|
|
39
|
+
* large `max` here was not network-bound at all.
|
|
40
|
+
*
|
|
41
|
+
* @returns {{ meanMs: number, p99Ms: number, maxMs: number }}
|
|
42
|
+
*/
|
|
43
|
+
export function eventLoopDelay() {
|
|
44
|
+
return {
|
|
45
|
+
meanMs: loopDelay.mean / NANOSECONDS_PER_MILLISECOND,
|
|
46
|
+
p99Ms: loopDelay.percentile(99) / NANOSECONDS_PER_MILLISECOND,
|
|
47
|
+
maxMs: loopDelay.max / NANOSECONDS_PER_MILLISECOND
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Start a fresh measurement window for the loop-delay histogram, so a reported
|
|
53
|
+
* figure describes one operation rather than the process's whole lifetime.
|
|
54
|
+
*
|
|
55
|
+
* @returns {void}
|
|
56
|
+
*/
|
|
57
|
+
export function resetEventLoopDelay() {
|
|
58
|
+
loopDelay.reset();
|
|
59
|
+
}
|
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file Canonical identity for a torrent source: its own infohash.
|
|
3
|
-
*
|
|
4
|
-
* A magnet URI and a `.torrent` file for the same content are different
|
|
5
|
-
* bytes, so naming a source by a hash of those bytes gave the same film two
|
|
6
|
-
* unrelated keys — one from a magnet, another from a `.torrent` — sharing
|
|
7
|
-
* neither the swarm, nor a cache, nor any work already done. Measured
|
|
8
|
-
* 2026-08-19: `a518ff46…` and `7ab2fb5d…` for one infohash `11f09299…`.
|
|
9
|
-
*
|
|
10
|
-
* The infohash is the one thing both forms carry for the same content, and
|
|
11
|
-
* both carry it synchronously — a magnet's `btih` needs no network, and a
|
|
12
|
-
* `.torrent` file's SHA-1 of its own `info` dictionary needs no metadata
|
|
13
|
-
* exchange either. `parse-torrent` reads either form without touching the
|
|
14
|
-
* network.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import parseTorrent from "parse-torrent";
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* @param {"magnet" | "torrent"} sourceType
|
|
21
|
-
* @param {string} source - Magnet URI, or base64-encoded `.torrent` bytes.
|
|
22
|
-
* @returns {Promise<string>} `torrent:<infohash>` — identical for a magnet
|
|
23
|
-
* and a `.torrent` file describing the same content.
|
|
24
|
-
*/
|
|
25
|
-
export async function deriveSourceKey(sourceType, source) {
|
|
26
|
-
const torrentId = sourceType === "torrent" ? Buffer.from(source, "base64") : source;
|
|
27
|
-
let parsed;
|
|
28
|
-
try {
|
|
29
|
-
parsed = await parseTorrent(torrentId);
|
|
30
|
-
} catch (error) {
|
|
31
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
32
|
-
throw new Error(`Could not read an infohash from this ${sourceType}: ${message}`);
|
|
33
|
-
}
|
|
34
|
-
if (!parsed?.infoHash) {
|
|
35
|
-
throw new Error(`Could not read an infohash from this ${sourceType}.`);
|
|
36
|
-
}
|
|
37
|
-
return `torrent:${parsed.infoHash}`;
|
|
38
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* @file Canonical identity for a torrent source: its own infohash.
|
|
3
|
+
*
|
|
4
|
+
* A magnet URI and a `.torrent` file for the same content are different
|
|
5
|
+
* bytes, so naming a source by a hash of those bytes gave the same film two
|
|
6
|
+
* unrelated keys — one from a magnet, another from a `.torrent` — sharing
|
|
7
|
+
* neither the swarm, nor a cache, nor any work already done. Measured
|
|
8
|
+
* 2026-08-19: `a518ff46…` and `7ab2fb5d…` for one infohash `11f09299…`.
|
|
9
|
+
*
|
|
10
|
+
* The infohash is the one thing both forms carry for the same content, and
|
|
11
|
+
* both carry it synchronously — a magnet's `btih` needs no network, and a
|
|
12
|
+
* `.torrent` file's SHA-1 of its own `info` dictionary needs no metadata
|
|
13
|
+
* exchange either. `parse-torrent` reads either form without touching the
|
|
14
|
+
* network.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import parseTorrent from "parse-torrent";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param {"magnet" | "torrent"} sourceType
|
|
21
|
+
* @param {string} source - Magnet URI, or base64-encoded `.torrent` bytes.
|
|
22
|
+
* @returns {Promise<string>} `torrent:<infohash>` — identical for a magnet
|
|
23
|
+
* and a `.torrent` file describing the same content.
|
|
24
|
+
*/
|
|
25
|
+
export async function deriveSourceKey(sourceType, source) {
|
|
26
|
+
const torrentId = sourceType === "torrent" ? Buffer.from(source, "base64") : source;
|
|
27
|
+
let parsed;
|
|
28
|
+
try {
|
|
29
|
+
parsed = await parseTorrent(torrentId);
|
|
30
|
+
} catch (error) {
|
|
31
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
32
|
+
throw new Error(`Could not read an infohash from this ${sourceType}: ${message}`);
|
|
33
|
+
}
|
|
34
|
+
if (!parsed?.infoHash) {
|
|
35
|
+
throw new Error(`Could not read an infohash from this ${sourceType}.`);
|
|
36
|
+
}
|
|
37
|
+
return `torrent:${parsed.infoHash}`;
|
|
38
|
+
}
|