@torrent-tv/proxy 2.87.4 → 2.87.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/commands/opsx/apply.md +155 -155
- package/.claude/commands/opsx/archive.md +160 -160
- package/.claude/commands/opsx/explore.md +174 -174
- package/.claude/commands/opsx/propose.md +109 -109
- package/.claude/commands/opsx/sync.md +143 -143
- package/.claude/skills/openspec-apply-change/SKILL.md +159 -159
- package/.claude/skills/openspec-archive-change/SKILL.md +117 -117
- package/.claude/skills/openspec-explore/SKILL.md +289 -289
- package/.claude/skills/openspec-propose/SKILL.md +113 -113
- package/.claude/skills/openspec-sync-specs/SKILL.md +147 -147
- package/.dockerignore +5 -5
- package/AGENTS.md +322 -322
- package/CHANGELOG.md +8 -0
- package/Dockerfile +30 -30
- package/LICENSE +16 -16
- package/README.md +369 -369
- package/assets/calibration/NOTICE.md +100 -100
- package/assets/diagnostics/sctpstate.gdb +31 -31
- package/bin/cli.js +684 -684
- package/bin/thread-pool.js +30 -30
- package/biome.json +411 -411
- package/docs/browser-proxy-contract.md +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 +1 -1
- package/public/.well-known/appspecific/com.chrome.devtools.json +1 -1
- package/research/delivery-wedge-stand-ladder-2026-09-13.md +329 -329
- package/research/double-spawn-2026-09-10.md +171 -171
- package/research/handover-2026-09-13.md +209 -209
- package/research/handover-reader-claims-removal-2026-09-12.md +166 -166
- package/research/piece-withdrawn-but-still-claimed-2026-09-12.md +175 -175
- package/research/preset-benchmark-size-scaling-2026-08-22.md +55 -55
- package/research/priority-map-is-the-truth-2026-09-12.md +225 -225
- package/routes/api/client-logs/post.js +86 -86
- package/routes/api/delivery-sink/get.js +72 -72
- package/routes/api/link-probe/get.js +51 -51
- package/routes/api/playback-plan/post.js +53 -53
- package/routes/api/sources/files/get.js +100 -100
- package/routes/api/sources/post.js +36 -36
- package/routes/api/sources/stats/get.js +132 -132
- package/routes/api/subtitles/get.js +230 -230
- package/routes/api/transcode-sessions/fragment-far/post.js +60 -60
- package/routes/api/transcode-sessions/net-report/post.js +129 -129
- package/routes/api/transcode-sessions/post.js +160 -160
- package/routes/api/transcode-sessions/progress/get.js +27 -27
- package/routes/api/transcode-sessions/release/post.js +41 -41
- package/routes/api/transcode-sessions/seek/post.js +63 -63
- package/routes/health/get.js +13 -13
- package/routes/healthz/get.js +13 -13
- package/routes/stream/get.js +361 -361
- package/routes/transcode/audio-file/get.js +64 -64
- package/routes/transcode/audio-warm/get.js +84 -84
- package/routes/transcode/session-file/get.js +52 -52
- package/routes/transcode/variant-file/get.js +95 -95
- package/routes/transcode/variant-warm/get.js +84 -84
- package/scripts/render-run-graph.js +173 -173
- package/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/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 +2 -2
- package/services/torrent/worker/background-fill.js +213 -213
- package/services/torrent/worker/channel.js +271 -271
- package/services/torrent/worker/fastest-wires.js +342 -342
- package/services/torrent/worker/handle-state.js +23 -23
- package/services/torrent/worker/held-bytes.js +155 -155
- package/services/torrent/worker/install-webrtc-shim.js +32 -32
- package/services/torrent/worker/piece-reader.js +1078 -1078
- package/services/torrent/worker/pool-adapter.js +1 -1
- package/services/torrent/worker/resume-warm.js +107 -107
- package/services/torrent/worker/webrtc-shim.js +219 -219
- package/services/transport/data-channel-handler.js +1356 -1356
- package/services/transport/delivery-probe.js +842 -842
- package/services/transport/health-collector.js +53 -53
- package/services/transport/nat-classifier.js +231 -231
- package/services/transport/packet-witness.js +808 -808
- package/services/transport/port-mapper.js +235 -235
- package/services/transport/registry-api.js +81 -81
- package/services/transport/tunnel-client.js +447 -447
- package/services/transport/usrsctp-state.js +175 -175
- package/services/transport/webrtc-manager.js +484 -484
- package/services/viewer/Assignments.js +340 -340
- package/services/viewer/PriorityMap.js +390 -390
- package/services/viewer/PriorityOrchestrator.js +325 -325
- package/services/viewer/Viewer.js +657 -657
- package/services/viewer/Viewers.js +368 -368
- package/services/viewer/active-output.js +37 -37
- package/services/viewer/audio-start.js +46 -46
- package/services/viewer/buffer-trend.js +81 -81
- package/services/viewer/choices.js +647 -647
- package/services/viewer/positions.js +126 -126
- package/services/viewer/report-intake.js +46 -46
- package/store/source-registry.js +71 -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/source-registry.test.js +18 -0
- package/test/split-a-stretch.test.js +139 -139
- package/test/stale-request-after-seek.test.js +158 -158
- package/test/startup-readings.test.js +124 -124
- package/test/stream-route.test.js +182 -182
- package/test/subtitle-cue-framing.test.js +200 -200
- package/test/subtitle-cue-source.test.js +102 -102
- package/test/subtitle-cue-walk.test.js +370 -370
- package/test/subtitle-cursor.test.js +63 -63
- package/test/subtitle-defaults.test.js +118 -118
- package/test/subtitle-language.test.js +252 -252
- package/test/subtitle-track-numbering.test.js +386 -386
- package/test/supply-margin.test.js +177 -177
- package/test/swarm-follows-readers.test.js +174 -174
- package/test/swarm-reach.test.js +130 -130
- package/test/swarm-selection.test.js +220 -220
- package/test/tail-duplication.test.js +167 -167
- package/test/the-channel-has-both-ends.test.js +91 -91
- package/test/the-torrent-thread-serves-bytes.test.js +107 -107
- package/test/thread-pool.test.js +53 -53
- package/test/timeline.test.js +84 -84
- package/test/torrent-contents.test.js +235 -235
- package/test/torrent-cost.test.js +71 -71
- package/test/torrent-files.test.js +221 -221
- package/test/torrent-naming.test.js +255 -255
- package/test/tracks-begin-together.test.js +209 -209
- package/test/tunnel-renewal.test.js +167 -167
- package/test/two-viewers-one-picture.test.js +407 -407
- package/test/upload-hurry.test.js +186 -186
- package/test/usrsctp-state.test.js +131 -131
- package/test/video-facts.test.js +102 -102
- package/test/viewer-assignments.test.js +143 -143
- package/test/viewer-outputs.test.js +285 -285
- package/test/viewer-presence.test.js +116 -116
- package/test/viewer-requests-admission.test.js +89 -89
- package/test/viewer-states.test.js +189 -189
- package/test/viewer-subtitle-subscription.test.js +96 -96
- package/test/viewer.test.js +139 -139
- package/test/viewing-generations.test.js +390 -390
- package/test/visible-rung.test.js +37 -37
- package/test/wait-ledger.test.js +100 -100
- package/test/webrtc-shim.test.js +149 -149
- package/test/wedge-certainty.test.js +131 -131
- package/test/withdraw-piece-claim.test.js +212 -212
- package/test/worker-channel.test.js +158 -158
- package/utils/client-log-file.js +151 -151
- package/utils/logger.js +312 -312
- package/utils/parse-range.js +25 -25
- package/utils/perf.js +59 -59
- package/{services/torrent → utils}/torrent-source-key.js +38 -38
|
@@ -1,153 +1,153 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file A store that is short of memory must inconvenience a read, never end a
|
|
3
|
-
* torrent.
|
|
4
|
-
*
|
|
5
|
-
* The field failure of 2026-09-11, in one sentence: the store could not hand
|
|
6
|
-
* out a block, threw, the throw travelled out through the torrent client's own
|
|
7
|
-
* write callback, and the client destroyed the torrent. For the rest of the
|
|
8
|
-
* process every read of that film answered `File 1 not found in
|
|
9
|
-
* torrent:d4022ff4…`, `/stats` reported `peers=0 connected of 1186 known`, and
|
|
10
|
-
* the viewer could not open anything until the addon was restarted.
|
|
11
|
-
*
|
|
12
|
-
* Three properties hold it shut, and each is checked here on its own:
|
|
13
|
-
*
|
|
14
|
-
* 1. an arriving piece is never refused — it has the disk;
|
|
15
|
-
* 2. a read on the torrent client's own path takes no block at all, so an
|
|
16
|
-
* upload can neither wait for memory nor be refused it;
|
|
17
|
-
* 3. a store between readers keeps room for one window, because that is what
|
|
18
|
-
* the next read asks for and the allowance is otherwise re-derived a
|
|
19
|
-
* minute later — twelve times slower than a claim gives up.
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
import test from "node:test";
|
|
23
|
-
import assert from "node:assert/strict";
|
|
24
|
-
import fs from "node:fs/promises";
|
|
25
|
-
import os from "node:os";
|
|
26
|
-
import path from "node:path";
|
|
27
|
-
import { SharedPieceStore } from "../services/storage/piece-store/shared-piece-store.js";
|
|
28
|
-
|
|
29
|
-
const PIECE = 1024;
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* @param {SharedPieceStore} store
|
|
33
|
-
* @param {number} index
|
|
34
|
-
* @returns {Promise<void>}
|
|
35
|
-
*/
|
|
36
|
-
const put = (store, index) =>
|
|
37
|
-
new Promise((resolve, reject) => {
|
|
38
|
-
store.put(index, Buffer.alloc(PIECE, index % 251), (error) => (error ? reject(error) : resolve()));
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* @param {SharedPieceStore} store
|
|
43
|
-
* @param {number} index
|
|
44
|
-
* @param {{ offset: number, length: number }} range
|
|
45
|
-
* @returns {Promise<Buffer>}
|
|
46
|
-
*/
|
|
47
|
-
const get = (store, index, range) =>
|
|
48
|
-
new Promise((resolve, reject) => {
|
|
49
|
-
store.get(index, range, (error, bytes) => (error ? reject(error) : resolve(bytes)));
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* @returns {Promise<string>}
|
|
54
|
-
*/
|
|
55
|
-
const directory = () => fs.mkdtemp(path.join(os.tmpdir(), "never-refuses-"));
|
|
56
|
-
|
|
57
|
-
test("a piece is never refused for want of memory", async () => {
|
|
58
|
-
const root = await directory();
|
|
59
|
-
// Every block held by a piece that may not leave. The ceiling never falls
|
|
60
|
-
// below two, so this is how a store with nothing to give is reached: what
|
|
61
|
-
// held the blocks in the field was two writes on their way to disk, and a pin
|
|
62
|
-
// reaches the same state deterministically.
|
|
63
|
-
//
|
|
64
|
-
// This one takes the store's own patience to run — it must be seen to give
|
|
65
|
-
// up and keep the piece anyway.
|
|
66
|
-
const store = new SharedPieceStore(PIECE, {
|
|
67
|
-
length: PIECE * 8,
|
|
68
|
-
memoryBytes: PIECE * 2,
|
|
69
|
-
path: root,
|
|
70
|
-
name: "no-block-to-be-had"
|
|
71
|
-
});
|
|
72
|
-
try {
|
|
73
|
-
await put(store, 0);
|
|
74
|
-
await put(store, 1);
|
|
75
|
-
store.pin(0);
|
|
76
|
-
store.pin(1);
|
|
77
|
-
|
|
78
|
-
await put(store, 3);
|
|
79
|
-
const stats = store.stats();
|
|
80
|
-
assert.equal(stats.spilled, 1, "the piece is on disk");
|
|
81
|
-
assert.equal(store.locate(3), null, "and not in memory, since no block could be had for it");
|
|
82
|
-
assert.equal(stats.resident, 2, "the pinned pieces were not taken from under their reader");
|
|
83
|
-
assert.ok(
|
|
84
|
-
stats.admittedWithoutSlot >= 1,
|
|
85
|
-
"and the store says so, rather than the torrent client saying it with a destroyed torrent"
|
|
86
|
-
);
|
|
87
|
-
const bytes = await get(store, 3, { offset: 0, length: PIECE });
|
|
88
|
-
assert.equal(bytes.length, PIECE, "and it reads back");
|
|
89
|
-
assert.equal(bytes[0], 3 % 251, "with its own contents");
|
|
90
|
-
} finally {
|
|
91
|
-
await new Promise((resolve) => store.destroy(resolve));
|
|
92
|
-
await fs.rm(root, { recursive: true, force: true });
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
test("a read for the torrent client takes no block and only its own range", async () => {
|
|
97
|
-
const root = await directory();
|
|
98
|
-
const store = new SharedPieceStore(PIECE, {
|
|
99
|
-
length: PIECE * 8,
|
|
100
|
-
memoryBytes: PIECE,
|
|
101
|
-
path: root,
|
|
102
|
-
name: "ranged-read"
|
|
103
|
-
});
|
|
104
|
-
try {
|
|
105
|
-
// Two fit; the third displaces the oldest, which goes to disk.
|
|
106
|
-
await put(store, 0);
|
|
107
|
-
await put(store, 1);
|
|
108
|
-
await put(store, 2);
|
|
109
|
-
const before = store.stats();
|
|
110
|
-
assert.equal(before.spilled, 1, "the first piece is on disk");
|
|
111
|
-
|
|
112
|
-
const wanted = 16;
|
|
113
|
-
const bytes = await get(store, 0, { offset: PIECE - wanted, length: wanted });
|
|
114
|
-
assert.equal(bytes.length, wanted, "only what was asked for comes back");
|
|
115
|
-
assert.equal(bytes[0], 0, "and it is that piece's own bytes");
|
|
116
|
-
|
|
117
|
-
const after = store.stats();
|
|
118
|
-
assert.equal(
|
|
119
|
-
after.blocksAllocated,
|
|
120
|
-
before.blocksAllocated,
|
|
121
|
-
"and answering it took no block: a peer asks for kilobytes and a piece here is megabytes"
|
|
122
|
-
);
|
|
123
|
-
assert.equal(after.resident, before.resident, "nothing was revived for it");
|
|
124
|
-
assert.equal(after.fromDisk, before.fromDisk + 1, "and the read is counted as coming from disk");
|
|
125
|
-
} finally {
|
|
126
|
-
await new Promise((resolve) => store.destroy(resolve));
|
|
127
|
-
await fs.rm(root, { recursive: true, force: true });
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
test("a store between readers keeps room for one window", async () => {
|
|
132
|
-
const root = await directory();
|
|
133
|
-
const store = new SharedPieceStore(PIECE, {
|
|
134
|
-
length: PIECE * 200,
|
|
135
|
-
memoryBytes: PIECE * 64,
|
|
136
|
-
path: root,
|
|
137
|
-
name: "between-readers"
|
|
138
|
-
});
|
|
139
|
-
try {
|
|
140
|
-
store.protectRange("read-1", 10, 21, 100);
|
|
141
|
-
const asked = store.wantedBytes;
|
|
142
|
-
assert.ok(asked >= PIECE * 12, "with a reader, the window it declared is asked for");
|
|
143
|
-
|
|
144
|
-
store.releaseProtection("read-1");
|
|
145
|
-
assert.ok(
|
|
146
|
-
store.wantedBytes >= PIECE * 12,
|
|
147
|
-
"and with the reader gone the floor is still one window — the next read asks for the same again"
|
|
148
|
-
);
|
|
149
|
-
} finally {
|
|
150
|
-
await new Promise((resolve) => store.destroy(resolve));
|
|
151
|
-
await fs.rm(root, { recursive: true, force: true });
|
|
152
|
-
}
|
|
153
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* @file A store that is short of memory must inconvenience a read, never end a
|
|
3
|
+
* torrent.
|
|
4
|
+
*
|
|
5
|
+
* The field failure of 2026-09-11, in one sentence: the store could not hand
|
|
6
|
+
* out a block, threw, the throw travelled out through the torrent client's own
|
|
7
|
+
* write callback, and the client destroyed the torrent. For the rest of the
|
|
8
|
+
* process every read of that film answered `File 1 not found in
|
|
9
|
+
* torrent:d4022ff4…`, `/stats` reported `peers=0 connected of 1186 known`, and
|
|
10
|
+
* the viewer could not open anything until the addon was restarted.
|
|
11
|
+
*
|
|
12
|
+
* Three properties hold it shut, and each is checked here on its own:
|
|
13
|
+
*
|
|
14
|
+
* 1. an arriving piece is never refused — it has the disk;
|
|
15
|
+
* 2. a read on the torrent client's own path takes no block at all, so an
|
|
16
|
+
* upload can neither wait for memory nor be refused it;
|
|
17
|
+
* 3. a store between readers keeps room for one window, because that is what
|
|
18
|
+
* the next read asks for and the allowance is otherwise re-derived a
|
|
19
|
+
* minute later — twelve times slower than a claim gives up.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import test from "node:test";
|
|
23
|
+
import assert from "node:assert/strict";
|
|
24
|
+
import fs from "node:fs/promises";
|
|
25
|
+
import os from "node:os";
|
|
26
|
+
import path from "node:path";
|
|
27
|
+
import { SharedPieceStore } from "../services/storage/piece-store/shared-piece-store.js";
|
|
28
|
+
|
|
29
|
+
const PIECE = 1024;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param {SharedPieceStore} store
|
|
33
|
+
* @param {number} index
|
|
34
|
+
* @returns {Promise<void>}
|
|
35
|
+
*/
|
|
36
|
+
const put = (store, index) =>
|
|
37
|
+
new Promise((resolve, reject) => {
|
|
38
|
+
store.put(index, Buffer.alloc(PIECE, index % 251), (error) => (error ? reject(error) : resolve()));
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @param {SharedPieceStore} store
|
|
43
|
+
* @param {number} index
|
|
44
|
+
* @param {{ offset: number, length: number }} range
|
|
45
|
+
* @returns {Promise<Buffer>}
|
|
46
|
+
*/
|
|
47
|
+
const get = (store, index, range) =>
|
|
48
|
+
new Promise((resolve, reject) => {
|
|
49
|
+
store.get(index, range, (error, bytes) => (error ? reject(error) : resolve(bytes)));
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @returns {Promise<string>}
|
|
54
|
+
*/
|
|
55
|
+
const directory = () => fs.mkdtemp(path.join(os.tmpdir(), "never-refuses-"));
|
|
56
|
+
|
|
57
|
+
test("a piece is never refused for want of memory", async () => {
|
|
58
|
+
const root = await directory();
|
|
59
|
+
// Every block held by a piece that may not leave. The ceiling never falls
|
|
60
|
+
// below two, so this is how a store with nothing to give is reached: what
|
|
61
|
+
// held the blocks in the field was two writes on their way to disk, and a pin
|
|
62
|
+
// reaches the same state deterministically.
|
|
63
|
+
//
|
|
64
|
+
// This one takes the store's own patience to run — it must be seen to give
|
|
65
|
+
// up and keep the piece anyway.
|
|
66
|
+
const store = new SharedPieceStore(PIECE, {
|
|
67
|
+
length: PIECE * 8,
|
|
68
|
+
memoryBytes: PIECE * 2,
|
|
69
|
+
path: root,
|
|
70
|
+
name: "no-block-to-be-had"
|
|
71
|
+
});
|
|
72
|
+
try {
|
|
73
|
+
await put(store, 0);
|
|
74
|
+
await put(store, 1);
|
|
75
|
+
store.pin(0);
|
|
76
|
+
store.pin(1);
|
|
77
|
+
|
|
78
|
+
await put(store, 3);
|
|
79
|
+
const stats = store.stats();
|
|
80
|
+
assert.equal(stats.spilled, 1, "the piece is on disk");
|
|
81
|
+
assert.equal(store.locate(3), null, "and not in memory, since no block could be had for it");
|
|
82
|
+
assert.equal(stats.resident, 2, "the pinned pieces were not taken from under their reader");
|
|
83
|
+
assert.ok(
|
|
84
|
+
stats.admittedWithoutSlot >= 1,
|
|
85
|
+
"and the store says so, rather than the torrent client saying it with a destroyed torrent"
|
|
86
|
+
);
|
|
87
|
+
const bytes = await get(store, 3, { offset: 0, length: PIECE });
|
|
88
|
+
assert.equal(bytes.length, PIECE, "and it reads back");
|
|
89
|
+
assert.equal(bytes[0], 3 % 251, "with its own contents");
|
|
90
|
+
} finally {
|
|
91
|
+
await new Promise((resolve) => store.destroy(resolve));
|
|
92
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("a read for the torrent client takes no block and only its own range", async () => {
|
|
97
|
+
const root = await directory();
|
|
98
|
+
const store = new SharedPieceStore(PIECE, {
|
|
99
|
+
length: PIECE * 8,
|
|
100
|
+
memoryBytes: PIECE,
|
|
101
|
+
path: root,
|
|
102
|
+
name: "ranged-read"
|
|
103
|
+
});
|
|
104
|
+
try {
|
|
105
|
+
// Two fit; the third displaces the oldest, which goes to disk.
|
|
106
|
+
await put(store, 0);
|
|
107
|
+
await put(store, 1);
|
|
108
|
+
await put(store, 2);
|
|
109
|
+
const before = store.stats();
|
|
110
|
+
assert.equal(before.spilled, 1, "the first piece is on disk");
|
|
111
|
+
|
|
112
|
+
const wanted = 16;
|
|
113
|
+
const bytes = await get(store, 0, { offset: PIECE - wanted, length: wanted });
|
|
114
|
+
assert.equal(bytes.length, wanted, "only what was asked for comes back");
|
|
115
|
+
assert.equal(bytes[0], 0, "and it is that piece's own bytes");
|
|
116
|
+
|
|
117
|
+
const after = store.stats();
|
|
118
|
+
assert.equal(
|
|
119
|
+
after.blocksAllocated,
|
|
120
|
+
before.blocksAllocated,
|
|
121
|
+
"and answering it took no block: a peer asks for kilobytes and a piece here is megabytes"
|
|
122
|
+
);
|
|
123
|
+
assert.equal(after.resident, before.resident, "nothing was revived for it");
|
|
124
|
+
assert.equal(after.fromDisk, before.fromDisk + 1, "and the read is counted as coming from disk");
|
|
125
|
+
} finally {
|
|
126
|
+
await new Promise((resolve) => store.destroy(resolve));
|
|
127
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test("a store between readers keeps room for one window", async () => {
|
|
132
|
+
const root = await directory();
|
|
133
|
+
const store = new SharedPieceStore(PIECE, {
|
|
134
|
+
length: PIECE * 200,
|
|
135
|
+
memoryBytes: PIECE * 64,
|
|
136
|
+
path: root,
|
|
137
|
+
name: "between-readers"
|
|
138
|
+
});
|
|
139
|
+
try {
|
|
140
|
+
store.protectRange("read-1", 10, 21, 100);
|
|
141
|
+
const asked = store.wantedBytes;
|
|
142
|
+
assert.ok(asked >= PIECE * 12, "with a reader, the window it declared is asked for");
|
|
143
|
+
|
|
144
|
+
store.releaseProtection("read-1");
|
|
145
|
+
assert.ok(
|
|
146
|
+
store.wantedBytes >= PIECE * 12,
|
|
147
|
+
"and with the reader gone the floor is still one window — the next read asks for the same again"
|
|
148
|
+
);
|
|
149
|
+
} finally {
|
|
150
|
+
await new Promise((resolve) => store.destroy(resolve));
|
|
151
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
152
|
+
}
|
|
153
|
+
});
|