@torrent-tv/proxy 2.87.4 → 2.87.5
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 +4 -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-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/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/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
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Release a consumer from a transcode session.
|
|
3
|
-
* When the last consumer is released the session is disposed automatically.
|
|
4
|
-
*
|
|
5
|
-
* POST /api/transcode-sessions/:sessionId/release
|
|
6
|
-
*
|
|
7
|
-
* @param {import("fastify").FastifyRequest} req
|
|
8
|
-
* @param {import("fastify").FastifyReply} reply
|
|
9
|
-
* @param {{ lifecycle: object }} deps
|
|
10
|
-
* @returns {Promise<void>}
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Extract a plain object from the request body, guarding against
|
|
15
|
-
* non-object payloads (arrays, primitives, null).
|
|
16
|
-
*
|
|
17
|
-
* @param {unknown} body
|
|
18
|
-
* @returns {Record<string, unknown>}
|
|
19
|
-
*/
|
|
20
|
-
function getPayload(body) {
|
|
21
|
-
if (body && typeof body === "object" && !Array.isArray(body)) {
|
|
22
|
-
return body;
|
|
23
|
-
}
|
|
24
|
-
return {};
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export async function handleApiTranscodeSessionReleasePost(req, reply, { lifecycle }) {
|
|
28
|
-
const sessionId = typeof req.params.sessionId === "string" ? req.params.sessionId : "";
|
|
29
|
-
const payload = getPayload(req.body);
|
|
30
|
-
const consumerId = typeof payload.consumerId === "string" ? payload.consumerId.trim() : "";
|
|
31
|
-
const reason = typeof payload.reason === "string" ? payload.reason.trim() : "";
|
|
32
|
-
if (!sessionId || !consumerId) {
|
|
33
|
-
return reply.code(400).send({ error: "sessionId and consumerId are required." });
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const released = await lifecycle.releaseSessionConsumer(sessionId, consumerId, reason);
|
|
37
|
-
if (!released) {
|
|
38
|
-
return reply.code(404).send({ error: "Transcode session was not found." });
|
|
39
|
-
}
|
|
40
|
-
return reply.send({ ok: true });
|
|
41
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Release a consumer from a transcode session.
|
|
3
|
+
* When the last consumer is released the session is disposed automatically.
|
|
4
|
+
*
|
|
5
|
+
* POST /api/transcode-sessions/:sessionId/release
|
|
6
|
+
*
|
|
7
|
+
* @param {import("fastify").FastifyRequest} req
|
|
8
|
+
* @param {import("fastify").FastifyReply} reply
|
|
9
|
+
* @param {{ lifecycle: object }} deps
|
|
10
|
+
* @returns {Promise<void>}
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Extract a plain object from the request body, guarding against
|
|
15
|
+
* non-object payloads (arrays, primitives, null).
|
|
16
|
+
*
|
|
17
|
+
* @param {unknown} body
|
|
18
|
+
* @returns {Record<string, unknown>}
|
|
19
|
+
*/
|
|
20
|
+
function getPayload(body) {
|
|
21
|
+
if (body && typeof body === "object" && !Array.isArray(body)) {
|
|
22
|
+
return body;
|
|
23
|
+
}
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function handleApiTranscodeSessionReleasePost(req, reply, { lifecycle }) {
|
|
28
|
+
const sessionId = typeof req.params.sessionId === "string" ? req.params.sessionId : "";
|
|
29
|
+
const payload = getPayload(req.body);
|
|
30
|
+
const consumerId = typeof payload.consumerId === "string" ? payload.consumerId.trim() : "";
|
|
31
|
+
const reason = typeof payload.reason === "string" ? payload.reason.trim() : "";
|
|
32
|
+
if (!sessionId || !consumerId) {
|
|
33
|
+
return reply.code(400).send({ error: "sessionId and consumerId are required." });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const released = await lifecycle.releaseSessionConsumer(sessionId, consumerId, reason);
|
|
37
|
+
if (!released) {
|
|
38
|
+
return reply.code(404).send({ error: "Transcode session was not found." });
|
|
39
|
+
}
|
|
40
|
+
return reply.send({ ok: true });
|
|
41
|
+
}
|
|
@@ -1,63 +1,63 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file POST /api/transcode-sessions/:sessionId/seek — the viewer's seek target.
|
|
3
|
-
*
|
|
4
|
-
* The browser reports WHERE THE VIEWER ACTUALLY SEEKED, explicitly, the moment
|
|
5
|
-
* the scrub ends. This is the only authoritative source of that intent: the
|
|
6
|
-
* position lives in the browser (`video.currentTime`) and nowhere else.
|
|
7
|
-
*
|
|
8
|
-
* Why this route exists at all: our synthetic VOD playlist advertises every
|
|
9
|
-
* segment of the file, but segments only exist once ffmpeg has produced them.
|
|
10
|
-
* A player is entitled by the HLS contract to fetch any advertised segment and
|
|
11
|
-
* get it immediately, so when one 503s it legitimately probes elsewhere —
|
|
12
|
-
* field log 2026-08-02 shows a correct target burst (#973..#985, the real seek)
|
|
13
|
-
* followed by scattered probing across the whole file (#125, #173, #251, #326,
|
|
14
|
-
* #478...). Inferring the target from that request stream made the encoder
|
|
15
|
-
* restart at #519 — a probe, not the seek — and the viewer waited ten seconds
|
|
16
|
-
* for a segment nobody wanted. Segment requests are data fetches, not commands;
|
|
17
|
-
* the seek target now arrives here instead of being guessed from them.
|
|
18
|
-
*
|
|
19
|
-
* Mirrors how Jellyfin/Plex handle server-side seeking (an explicit start
|
|
20
|
-
* position from the client), rather than heuristics over request patterns.
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* @param {import("fastify").FastifyRequest} req
|
|
25
|
-
* @param {import("fastify").FastifyReply} reply
|
|
26
|
-
* @param {{ viewerRequests: object }} deps
|
|
27
|
-
* @returns {Promise<void>}
|
|
28
|
-
*/
|
|
29
|
-
export async function handleApiTranscodeSessionSeekPost(req, reply, { viewerRequests }) {
|
|
30
|
-
const sessionId = req.params?.sessionId;
|
|
31
|
-
const body = req.body && typeof req.body === "object" ? req.body : {};
|
|
32
|
-
const positionSeconds = Number(body.positionSeconds);
|
|
33
|
-
|
|
34
|
-
if (!Number.isFinite(positionSeconds) || positionSeconds < 0) {
|
|
35
|
-
reply.code(400);
|
|
36
|
-
return reply.send({ error: "positionSeconds must be a non-negative number." });
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Who moved. A session can be shared, and the seeking viewer's own position
|
|
40
|
-
// has to move with them or their next request is judged against where they
|
|
41
|
-
// were before the jump.
|
|
42
|
-
const consumerId = typeof body.consumerId === "string" ? body.consumerId.trim() : "";
|
|
43
|
-
if (!consumerId) {
|
|
44
|
-
reply.code(400);
|
|
45
|
-
return reply.send({ error: "consumerId is required." });
|
|
46
|
-
}
|
|
47
|
-
// Which viewing their requests now belong to. The page raises this before it
|
|
48
|
-
// sends anything and stamps every request with it, so a request that crosses
|
|
49
|
-
// this message is still read against the viewing it was made in. A page that
|
|
50
|
-
// states nothing leaves the generation where it is.
|
|
51
|
-
const generation = Number(body.generation);
|
|
52
|
-
const applied = viewerRequests.requestSeek(sessionId, positionSeconds, consumerId, generation);
|
|
53
|
-
if (!applied) {
|
|
54
|
-
// Unknown or disposed session — nothing to steer. Not an error worth
|
|
55
|
-
// surfacing to the viewer: the seek will be handled by whatever session
|
|
56
|
-
// replaces it.
|
|
57
|
-
reply.code(404);
|
|
58
|
-
return reply.send({ error: "No such transcode session." });
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
reply.code(204);
|
|
62
|
-
return reply.send();
|
|
63
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* @file POST /api/transcode-sessions/:sessionId/seek — the viewer's seek target.
|
|
3
|
+
*
|
|
4
|
+
* The browser reports WHERE THE VIEWER ACTUALLY SEEKED, explicitly, the moment
|
|
5
|
+
* the scrub ends. This is the only authoritative source of that intent: the
|
|
6
|
+
* position lives in the browser (`video.currentTime`) and nowhere else.
|
|
7
|
+
*
|
|
8
|
+
* Why this route exists at all: our synthetic VOD playlist advertises every
|
|
9
|
+
* segment of the file, but segments only exist once ffmpeg has produced them.
|
|
10
|
+
* A player is entitled by the HLS contract to fetch any advertised segment and
|
|
11
|
+
* get it immediately, so when one 503s it legitimately probes elsewhere —
|
|
12
|
+
* field log 2026-08-02 shows a correct target burst (#973..#985, the real seek)
|
|
13
|
+
* followed by scattered probing across the whole file (#125, #173, #251, #326,
|
|
14
|
+
* #478...). Inferring the target from that request stream made the encoder
|
|
15
|
+
* restart at #519 — a probe, not the seek — and the viewer waited ten seconds
|
|
16
|
+
* for a segment nobody wanted. Segment requests are data fetches, not commands;
|
|
17
|
+
* the seek target now arrives here instead of being guessed from them.
|
|
18
|
+
*
|
|
19
|
+
* Mirrors how Jellyfin/Plex handle server-side seeking (an explicit start
|
|
20
|
+
* position from the client), rather than heuristics over request patterns.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @param {import("fastify").FastifyRequest} req
|
|
25
|
+
* @param {import("fastify").FastifyReply} reply
|
|
26
|
+
* @param {{ viewerRequests: object }} deps
|
|
27
|
+
* @returns {Promise<void>}
|
|
28
|
+
*/
|
|
29
|
+
export async function handleApiTranscodeSessionSeekPost(req, reply, { viewerRequests }) {
|
|
30
|
+
const sessionId = req.params?.sessionId;
|
|
31
|
+
const body = req.body && typeof req.body === "object" ? req.body : {};
|
|
32
|
+
const positionSeconds = Number(body.positionSeconds);
|
|
33
|
+
|
|
34
|
+
if (!Number.isFinite(positionSeconds) || positionSeconds < 0) {
|
|
35
|
+
reply.code(400);
|
|
36
|
+
return reply.send({ error: "positionSeconds must be a non-negative number." });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Who moved. A session can be shared, and the seeking viewer's own position
|
|
40
|
+
// has to move with them or their next request is judged against where they
|
|
41
|
+
// were before the jump.
|
|
42
|
+
const consumerId = typeof body.consumerId === "string" ? body.consumerId.trim() : "";
|
|
43
|
+
if (!consumerId) {
|
|
44
|
+
reply.code(400);
|
|
45
|
+
return reply.send({ error: "consumerId is required." });
|
|
46
|
+
}
|
|
47
|
+
// Which viewing their requests now belong to. The page raises this before it
|
|
48
|
+
// sends anything and stamps every request with it, so a request that crosses
|
|
49
|
+
// this message is still read against the viewing it was made in. A page that
|
|
50
|
+
// states nothing leaves the generation where it is.
|
|
51
|
+
const generation = Number(body.generation);
|
|
52
|
+
const applied = viewerRequests.requestSeek(sessionId, positionSeconds, consumerId, generation);
|
|
53
|
+
if (!applied) {
|
|
54
|
+
// Unknown or disposed session — nothing to steer. Not an error worth
|
|
55
|
+
// surfacing to the viewer: the seek will be handled by whatever session
|
|
56
|
+
// replaces it.
|
|
57
|
+
reply.code(404);
|
|
58
|
+
return reply.send({ error: "No such transcode session." });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
reply.code(204);
|
|
62
|
+
return reply.send();
|
|
63
|
+
}
|
package/routes/health/get.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Basic liveness check for load balancers and container orchestrators.
|
|
3
|
-
*
|
|
4
|
-
* GET /health
|
|
5
|
-
*
|
|
6
|
-
* @param {import("fastify").FastifyRequest} _req
|
|
7
|
-
* @param {import("fastify").FastifyReply} reply
|
|
8
|
-
* @param {{ version: string }} deps
|
|
9
|
-
* @returns {Promise<void>}
|
|
10
|
-
*/
|
|
11
|
-
export async function handleHealthGet(_req, reply, { version } = {}) {
|
|
12
|
-
return reply.send({ ok: true, version });
|
|
13
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Basic liveness check for load balancers and container orchestrators.
|
|
3
|
+
*
|
|
4
|
+
* GET /health
|
|
5
|
+
*
|
|
6
|
+
* @param {import("fastify").FastifyRequest} _req
|
|
7
|
+
* @param {import("fastify").FastifyReply} reply
|
|
8
|
+
* @param {{ version: string }} deps
|
|
9
|
+
* @returns {Promise<void>}
|
|
10
|
+
*/
|
|
11
|
+
export async function handleHealthGet(_req, reply, { version } = {}) {
|
|
12
|
+
return reply.send({ ok: true, version });
|
|
13
|
+
}
|
package/routes/healthz/get.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Docker / Kubernetes readiness probe endpoint.
|
|
3
|
-
*
|
|
4
|
-
* GET /healthz
|
|
5
|
-
*
|
|
6
|
-
* @param {import("fastify").FastifyRequest} _req
|
|
7
|
-
* @param {import("fastify").FastifyReply} reply
|
|
8
|
-
* @param {{ version: string }} deps
|
|
9
|
-
* @returns {Promise<void>}
|
|
10
|
-
*/
|
|
11
|
-
export async function handleHealthzGet(_req, reply, { version } = {}) {
|
|
12
|
-
return reply.send({ ok: true, version });
|
|
13
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Docker / Kubernetes readiness probe endpoint.
|
|
3
|
+
*
|
|
4
|
+
* GET /healthz
|
|
5
|
+
*
|
|
6
|
+
* @param {import("fastify").FastifyRequest} _req
|
|
7
|
+
* @param {import("fastify").FastifyReply} reply
|
|
8
|
+
* @param {{ version: string }} deps
|
|
9
|
+
* @returns {Promise<void>}
|
|
10
|
+
*/
|
|
11
|
+
export async function handleHealthzGet(_req, reply, { version } = {}) {
|
|
12
|
+
return reply.send({ ok: true, version });
|
|
13
|
+
}
|