@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,89 +1,89 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file A run is POSITIONED where the player was told the segment begins.
|
|
3
|
-
*
|
|
4
|
-
* The companion of `cuts-follow-published-grid`, and the half that was missing.
|
|
5
|
-
* 2.45.0 moved the cut list onto the published table and left the position on
|
|
6
|
-
* the live one — but `-segment_times` are measured from wherever the run really
|
|
7
|
-
* began, so any distance between the two moves EVERY cut of that run by it. The
|
|
8
|
-
* live table keeps being corrected, and the corrections run backwards, so each
|
|
9
|
-
* restart began a little earlier than the grid its cuts were stated on and the
|
|
10
|
-
* distance accumulated across restarts.
|
|
11
|
-
*
|
|
12
|
-
* Field 2026-08-21, `JUFD665.mp4` (MP4, copy path, index read cleanly): after
|
|
13
|
-
* one seek restart a produced segment held the boundary two places before its
|
|
14
|
-
* own number — 16.684 s, which is 2.0000 segments — and after the next restart,
|
|
15
|
-
* four places, 33.5 s. The player's buffer then stopped extending at all,
|
|
16
|
-
* because every fragment's content landed before the time its playlist entry
|
|
17
|
-
* named: `bufferEnd` stood still at 4571.1 s through four `frag-far` warnings
|
|
18
|
-
* until hls.js gave up and jumped the viewer 16.8 s forward.
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
import assert from "node:assert/strict";
|
|
22
|
-
import { Timeline } from "../services/encode/output/Timeline.js";
|
|
23
|
-
import test from "node:test";
|
|
24
|
-
|
|
25
|
-
import { wireOutputs } from "../services/server/wire-outputs.js";
|
|
26
|
-
import { segmentCutTimesFrom } from "../services/encode/run-command.js";
|
|
27
|
-
import { describeGridDrift } from "../services/encode/OutputTimes.js";
|
|
28
|
-
|
|
29
|
-
/** What the playlist in the player's hands says. */
|
|
30
|
-
const PUBLISHED = [0, 8.342, 16.684, 25.026, 33.368, 41.71];
|
|
31
|
-
/** The same grid after produced segments moved two of its cuts backwards. */
|
|
32
|
-
const CORRECTED = [0, 8.342, 14.682, 25.026, 31.366, 41.71];
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* @returns {{ manager: object, session: object }}
|
|
36
|
-
*/
|
|
37
|
-
function sessionWithDriftedGrid() {
|
|
38
|
-
const manager = wireOutputs({
|
|
39
|
-
enabled: true,
|
|
40
|
-
ffmpegBin: "ffmpeg",
|
|
41
|
-
localBindHost: "127.0.0.1",
|
|
42
|
-
localPort: 9090
|
|
43
|
-
});
|
|
44
|
-
const session = {
|
|
45
|
-
id: "picture",
|
|
46
|
-
timeline: new Timeline({ boundaries: [...CORRECTED], published: [...PUBLISHED], cutGrid: "uniform" })
|
|
47
|
-
};
|
|
48
|
-
return { manager, session };
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
test("a run starts at the time the player was told, not at the corrected one", () => {
|
|
52
|
-
const { manager, session } = sessionWithDriftedGrid();
|
|
53
|
-
assert.equal(manager.outputTimes.runStartTimeFor(session, 2), 16.684);
|
|
54
|
-
assert.notEqual(manager.outputTimes.runStartTimeFor(session, 2), session.timeline.boundaries[2]);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
test("position and cut list come from the same table", () => {
|
|
58
|
-
const { manager, session } = sessionWithDriftedGrid();
|
|
59
|
-
const grid = manager.outputTimes.publishedGridFor(session);
|
|
60
|
-
const start = manager.outputTimes.runStartTimeFor(session, 2);
|
|
61
|
-
// The cut list is stated as offsets from where the run begins. Adding the
|
|
62
|
-
// position back must land on the published boundaries exactly — which is the
|
|
63
|
-
// property that was false while the two came from different tables.
|
|
64
|
-
const absolute = segmentCutTimesFrom(grid, 2).map((offset) => Number((start + offset).toFixed(3)));
|
|
65
|
-
assert.deepEqual(absolute, [25.026, 33.368]);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test("a session that published no grid positions on the live one", () => {
|
|
69
|
-
const { manager } = sessionWithDriftedGrid();
|
|
70
|
-
const session = { id: "no-playlist", timeline: new Timeline({ boundaries: [...CORRECTED], published: [], cutGrid: "uniform" }) };
|
|
71
|
-
assert.equal(manager.outputTimes.runStartTimeFor(session, 2), CORRECTED[2]);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
test("an index beyond the table is clamped rather than returning nothing", () => {
|
|
75
|
-
const { manager, session } = sessionWithDriftedGrid();
|
|
76
|
-
assert.equal(manager.outputTimes.runStartTimeFor(session, 9999), PUBLISHED[PUBLISHED.length - 1]);
|
|
77
|
-
assert.equal(manager.outputTimes.runStartTimeFor(session, -3), PUBLISHED[0]);
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
test("the drift between the two tables is stated in full", () => {
|
|
81
|
-
assert.equal(describeGridDrift(PUBLISHED, PUBLISHED), "identical");
|
|
82
|
-
assert.equal(
|
|
83
|
-
describeGridDrift(PUBLISHED, CORRECTED),
|
|
84
|
-
"2 of 6 boundaries apart, worst 2.002s at #2"
|
|
85
|
-
);
|
|
86
|
-
assert.equal(describeGridDrift(PUBLISHED, [0, 1]), "a different length (6 against 2)");
|
|
87
|
-
assert.equal(describeGridDrift(null, CORRECTED), "not comparable");
|
|
88
|
-
assert.equal(describeGridDrift([], []), "not comparable");
|
|
89
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* @file A run is POSITIONED where the player was told the segment begins.
|
|
3
|
+
*
|
|
4
|
+
* The companion of `cuts-follow-published-grid`, and the half that was missing.
|
|
5
|
+
* 2.45.0 moved the cut list onto the published table and left the position on
|
|
6
|
+
* the live one — but `-segment_times` are measured from wherever the run really
|
|
7
|
+
* began, so any distance between the two moves EVERY cut of that run by it. The
|
|
8
|
+
* live table keeps being corrected, and the corrections run backwards, so each
|
|
9
|
+
* restart began a little earlier than the grid its cuts were stated on and the
|
|
10
|
+
* distance accumulated across restarts.
|
|
11
|
+
*
|
|
12
|
+
* Field 2026-08-21, `JUFD665.mp4` (MP4, copy path, index read cleanly): after
|
|
13
|
+
* one seek restart a produced segment held the boundary two places before its
|
|
14
|
+
* own number — 16.684 s, which is 2.0000 segments — and after the next restart,
|
|
15
|
+
* four places, 33.5 s. The player's buffer then stopped extending at all,
|
|
16
|
+
* because every fragment's content landed before the time its playlist entry
|
|
17
|
+
* named: `bufferEnd` stood still at 4571.1 s through four `frag-far` warnings
|
|
18
|
+
* until hls.js gave up and jumped the viewer 16.8 s forward.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import assert from "node:assert/strict";
|
|
22
|
+
import { Timeline } from "../services/encode/output/Timeline.js";
|
|
23
|
+
import test from "node:test";
|
|
24
|
+
|
|
25
|
+
import { wireOutputs } from "../services/server/wire-outputs.js";
|
|
26
|
+
import { segmentCutTimesFrom } from "../services/encode/run-command.js";
|
|
27
|
+
import { describeGridDrift } from "../services/encode/OutputTimes.js";
|
|
28
|
+
|
|
29
|
+
/** What the playlist in the player's hands says. */
|
|
30
|
+
const PUBLISHED = [0, 8.342, 16.684, 25.026, 33.368, 41.71];
|
|
31
|
+
/** The same grid after produced segments moved two of its cuts backwards. */
|
|
32
|
+
const CORRECTED = [0, 8.342, 14.682, 25.026, 31.366, 41.71];
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @returns {{ manager: object, session: object }}
|
|
36
|
+
*/
|
|
37
|
+
function sessionWithDriftedGrid() {
|
|
38
|
+
const manager = wireOutputs({
|
|
39
|
+
enabled: true,
|
|
40
|
+
ffmpegBin: "ffmpeg",
|
|
41
|
+
localBindHost: "127.0.0.1",
|
|
42
|
+
localPort: 9090
|
|
43
|
+
});
|
|
44
|
+
const session = {
|
|
45
|
+
id: "picture",
|
|
46
|
+
timeline: new Timeline({ boundaries: [...CORRECTED], published: [...PUBLISHED], cutGrid: "uniform" })
|
|
47
|
+
};
|
|
48
|
+
return { manager, session };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
test("a run starts at the time the player was told, not at the corrected one", () => {
|
|
52
|
+
const { manager, session } = sessionWithDriftedGrid();
|
|
53
|
+
assert.equal(manager.outputTimes.runStartTimeFor(session, 2), 16.684);
|
|
54
|
+
assert.notEqual(manager.outputTimes.runStartTimeFor(session, 2), session.timeline.boundaries[2]);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("position and cut list come from the same table", () => {
|
|
58
|
+
const { manager, session } = sessionWithDriftedGrid();
|
|
59
|
+
const grid = manager.outputTimes.publishedGridFor(session);
|
|
60
|
+
const start = manager.outputTimes.runStartTimeFor(session, 2);
|
|
61
|
+
// The cut list is stated as offsets from where the run begins. Adding the
|
|
62
|
+
// position back must land on the published boundaries exactly — which is the
|
|
63
|
+
// property that was false while the two came from different tables.
|
|
64
|
+
const absolute = segmentCutTimesFrom(grid, 2).map((offset) => Number((start + offset).toFixed(3)));
|
|
65
|
+
assert.deepEqual(absolute, [25.026, 33.368]);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("a session that published no grid positions on the live one", () => {
|
|
69
|
+
const { manager } = sessionWithDriftedGrid();
|
|
70
|
+
const session = { id: "no-playlist", timeline: new Timeline({ boundaries: [...CORRECTED], published: [], cutGrid: "uniform" }) };
|
|
71
|
+
assert.equal(manager.outputTimes.runStartTimeFor(session, 2), CORRECTED[2]);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("an index beyond the table is clamped rather than returning nothing", () => {
|
|
75
|
+
const { manager, session } = sessionWithDriftedGrid();
|
|
76
|
+
assert.equal(manager.outputTimes.runStartTimeFor(session, 9999), PUBLISHED[PUBLISHED.length - 1]);
|
|
77
|
+
assert.equal(manager.outputTimes.runStartTimeFor(session, -3), PUBLISHED[0]);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("the drift between the two tables is stated in full", () => {
|
|
81
|
+
assert.equal(describeGridDrift(PUBLISHED, PUBLISHED), "identical");
|
|
82
|
+
assert.equal(
|
|
83
|
+
describeGridDrift(PUBLISHED, CORRECTED),
|
|
84
|
+
"2 of 6 boundaries apart, worst 2.002s at #2"
|
|
85
|
+
);
|
|
86
|
+
assert.equal(describeGridDrift(PUBLISHED, [0, 1]), "a different length (6 against 2)");
|
|
87
|
+
assert.equal(describeGridDrift(null, CORRECTED), "not comparable");
|
|
88
|
+
assert.equal(describeGridDrift([], []), "not comparable");
|
|
89
|
+
});
|
|
@@ -1,65 +1,65 @@
|
|
|
1
|
-
import test from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import { RunProgress } from "../services/encode/RunProgress.js";
|
|
4
|
-
import { EncodeOrchestrator } from "../services/encode/EncodeOrchestrator.js";
|
|
5
|
-
|
|
6
|
-
function orchestrator() {
|
|
7
|
-
return new EncodeOrchestrator({
|
|
8
|
-
maxRunsFor: () => 2,
|
|
9
|
-
makeRun: () => null,
|
|
10
|
-
segmentSeconds: 4,
|
|
11
|
-
logger: { info() {}, warn() {} }
|
|
12
|
-
});
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
test("a run rebases ffmpeg's relative clock onto its own source position", () => {
|
|
16
|
-
let now = 10;
|
|
17
|
-
const progress = new RunProgress({ startSeconds: 600, totalSeconds: 1000, now: () => now });
|
|
18
|
-
now = 20;
|
|
19
|
-
progress.note({ processedSeconds: 12.5, speed: "2.0x" });
|
|
20
|
-
|
|
21
|
-
assert.deepEqual(progress.snapshot(), {
|
|
22
|
-
processedSeconds: 612.5,
|
|
23
|
-
startPositionSeconds: 600,
|
|
24
|
-
totalSeconds: 1000,
|
|
25
|
-
percent: 3.125,
|
|
26
|
-
remainingSeconds: 387.5,
|
|
27
|
-
speed: "2.0x",
|
|
28
|
-
updatedAt: 20
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test("two runs of one output retain separate clocks", () => {
|
|
33
|
-
const made = orchestrator();
|
|
34
|
-
const first = { from: 0, to: 49, isAlive: true, progress: new RunProgress({ startSeconds: 0 }) };
|
|
35
|
-
const second = { from: 50, to: 99, isAlive: true, progress: new RunProgress({ startSeconds: 200 }) };
|
|
36
|
-
first.progress.note({ processedSeconds: 40, speed: "1.0x" });
|
|
37
|
-
second.progress.note({ processedSeconds: 8, speed: "2.0x" });
|
|
38
|
-
made.adopt("picture", first);
|
|
39
|
-
made.adopt("picture", second);
|
|
40
|
-
|
|
41
|
-
assert.equal(made.progressOf("picture", 20).processedSeconds, 40);
|
|
42
|
-
assert.equal(made.progressOf("picture", 60).processedSeconds, 208);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
test("the last run's progress remains available after it ends", () => {
|
|
46
|
-
const made = orchestrator();
|
|
47
|
-
const progress = new RunProgress({ startSeconds: 100 });
|
|
48
|
-
progress.note({ processedSeconds: 5, speed: "1.5x" });
|
|
49
|
-
const run = { from: 25, to: 30, isAlive: false, state: "ENDED_COMPLETE", progress };
|
|
50
|
-
made.adopt("picture", run);
|
|
51
|
-
made.noteEnded({
|
|
52
|
-
address: "picture",
|
|
53
|
-
run,
|
|
54
|
-
ending: "complete",
|
|
55
|
-
because: "the assigned stretch was completed",
|
|
56
|
-
from: 25,
|
|
57
|
-
to: 30,
|
|
58
|
-
reached: 30,
|
|
59
|
-
livedMs: 1000,
|
|
60
|
-
normal: true,
|
|
61
|
-
lastError: ""
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
assert.equal(made.progressOf("picture").processedSeconds, 105);
|
|
65
|
-
});
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { RunProgress } from "../services/encode/RunProgress.js";
|
|
4
|
+
import { EncodeOrchestrator } from "../services/encode/EncodeOrchestrator.js";
|
|
5
|
+
|
|
6
|
+
function orchestrator() {
|
|
7
|
+
return new EncodeOrchestrator({
|
|
8
|
+
maxRunsFor: () => 2,
|
|
9
|
+
makeRun: () => null,
|
|
10
|
+
segmentSeconds: 4,
|
|
11
|
+
logger: { info() {}, warn() {} }
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
test("a run rebases ffmpeg's relative clock onto its own source position", () => {
|
|
16
|
+
let now = 10;
|
|
17
|
+
const progress = new RunProgress({ startSeconds: 600, totalSeconds: 1000, now: () => now });
|
|
18
|
+
now = 20;
|
|
19
|
+
progress.note({ processedSeconds: 12.5, speed: "2.0x" });
|
|
20
|
+
|
|
21
|
+
assert.deepEqual(progress.snapshot(), {
|
|
22
|
+
processedSeconds: 612.5,
|
|
23
|
+
startPositionSeconds: 600,
|
|
24
|
+
totalSeconds: 1000,
|
|
25
|
+
percent: 3.125,
|
|
26
|
+
remainingSeconds: 387.5,
|
|
27
|
+
speed: "2.0x",
|
|
28
|
+
updatedAt: 20
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("two runs of one output retain separate clocks", () => {
|
|
33
|
+
const made = orchestrator();
|
|
34
|
+
const first = { from: 0, to: 49, isAlive: true, progress: new RunProgress({ startSeconds: 0 }) };
|
|
35
|
+
const second = { from: 50, to: 99, isAlive: true, progress: new RunProgress({ startSeconds: 200 }) };
|
|
36
|
+
first.progress.note({ processedSeconds: 40, speed: "1.0x" });
|
|
37
|
+
second.progress.note({ processedSeconds: 8, speed: "2.0x" });
|
|
38
|
+
made.adopt("picture", first);
|
|
39
|
+
made.adopt("picture", second);
|
|
40
|
+
|
|
41
|
+
assert.equal(made.progressOf("picture", 20).processedSeconds, 40);
|
|
42
|
+
assert.equal(made.progressOf("picture", 60).processedSeconds, 208);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("the last run's progress remains available after it ends", () => {
|
|
46
|
+
const made = orchestrator();
|
|
47
|
+
const progress = new RunProgress({ startSeconds: 100 });
|
|
48
|
+
progress.note({ processedSeconds: 5, speed: "1.5x" });
|
|
49
|
+
const run = { from: 25, to: 30, isAlive: false, state: "ENDED_COMPLETE", progress };
|
|
50
|
+
made.adopt("picture", run);
|
|
51
|
+
made.noteEnded({
|
|
52
|
+
address: "picture",
|
|
53
|
+
run,
|
|
54
|
+
ending: "complete",
|
|
55
|
+
because: "the assigned stretch was completed",
|
|
56
|
+
from: 25,
|
|
57
|
+
to: 30,
|
|
58
|
+
reached: 30,
|
|
59
|
+
livedMs: 1000,
|
|
60
|
+
normal: true,
|
|
61
|
+
lastError: ""
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
assert.equal(made.progressOf("picture").processedSeconds, 105);
|
|
65
|
+
});
|