@torrent-tv/proxy 2.87.7 → 2.87.8
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/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/audio-tracks/post.js +29 -29
- 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/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/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/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 +21 -8
- 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 -71
- package/test/asset-paths.test.js +28 -28
- 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/data-channel-viewer-lifecycle.test.js +92 -0
- 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 -18
- package/test/split-a-stretch.test.js +139 -139
- package/test/stale-request-after-seek.test.js +158 -158
- package/test/startup-readings.test.js +124 -124
- package/test/stream-route.test.js +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/utils/torrent-source-key.js +38 -38
|
@@ -1,162 +1,162 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file Which heights are on the menu, and when that answer may be reused.
|
|
3
|
-
*
|
|
4
|
-
* The arithmetic is `EncodeCost` and is checked next door. What is checked here
|
|
5
|
-
* is the part that is not arithmetic: whose answer it is, what may never be
|
|
6
|
-
* withdrawn, and the cache — which is the whole reason this is an object, and
|
|
7
|
-
* which had no check of any kind while it lived in the session manager.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import test from "node:test";
|
|
11
|
-
import assert from "node:assert/strict";
|
|
12
|
-
import { EncodeCost } from "../services/encode/quality/EncodeCost.js";
|
|
13
|
-
import { QualityOffer } from "../services/encode/quality/QualityOffer.js";
|
|
14
|
-
import { startRunOn } from "./helpers/encode-run.js";
|
|
15
|
-
import { outputSpec } from "./helpers/output-spec.js";
|
|
16
|
-
import { runStateOf } from "../services/encode/encode-run-state.js";
|
|
17
|
-
import { qualityStateOf } from "../services/encode/quality/OutputQualityState.js";
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* A session with one run going.
|
|
21
|
-
*
|
|
22
|
-
* @returns {object}
|
|
23
|
-
*/
|
|
24
|
-
function sessionProducing() {
|
|
25
|
-
const session = {
|
|
26
|
-
id: "1111111122223333",
|
|
27
|
-
state: "ready",
|
|
28
|
-
spec: outputSpec(),
|
|
29
|
-
file: { key: "torrent:abc:0", name: "film.mkv", width: 1920, height: 1080 },
|
|
30
|
-
output: { encodeWidth: 0, encodeHeight: 0, outputFps: 25, softwarePreset: null },
|
|
31
|
-
progress: { processedSeconds: 0 },
|
|
32
|
-
runs: new Set()
|
|
33
|
-
};
|
|
34
|
-
startRunOn(session, { from: 0, speedX: 1 });
|
|
35
|
-
return session;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* A picture, and the offer's cache in play.
|
|
40
|
-
*
|
|
41
|
-
* @param {object} [supply]
|
|
42
|
-
* @returns {{ offer: QualityOffer, picture: object, computed: () => number, supply: object }}
|
|
43
|
-
*/
|
|
44
|
-
function offerOver(
|
|
45
|
-
supply = { requiredSpeed: null, megabytesPerSecond: null, costPerMegabyte: null },
|
|
46
|
-
occupancyKnownFor = () => true
|
|
47
|
-
) {
|
|
48
|
-
const picture = sessionProducing();
|
|
49
|
-
const outputs = {
|
|
50
|
-
familyOf: () => [picture],
|
|
51
|
-
pictureOf: () => picture,
|
|
52
|
-
variantHeightOf: () => 0,
|
|
53
|
-
sessionsOn: () => []
|
|
54
|
-
};
|
|
55
|
-
const cost = new EncodeCost({
|
|
56
|
-
outputs,
|
|
57
|
-
host: () => ({ benchmark: null, decodeModel: null, contentionPenalties: null, availability: null }),
|
|
58
|
-
runningEncoders: () => 0,
|
|
59
|
-
encodersRunningNow: () => 0,
|
|
60
|
-
torrentCostSecFor: () => 0,
|
|
61
|
-
boundBy: async () => "cpu",
|
|
62
|
-
runsFor: (session) => [...(session.runs ?? [])],
|
|
63
|
-
stateFor: (session) => runStateOf(session.runs),
|
|
64
|
-
progressFor: (session) => session.progress ?? null
|
|
65
|
-
});
|
|
66
|
-
let computed = 0;
|
|
67
|
-
const real = cost.sustainableHeights.bind(cost);
|
|
68
|
-
cost.sustainableHeights = (params) => {
|
|
69
|
-
computed += 1;
|
|
70
|
-
return real(params);
|
|
71
|
-
};
|
|
72
|
-
const offer = new QualityOffer({
|
|
73
|
-
encodeCost: cost,
|
|
74
|
-
outputs,
|
|
75
|
-
stateFor: (session) => runStateOf(session.runs),
|
|
76
|
-
heightsOnScreen: () => [],
|
|
77
|
-
supplyFor: () => supply,
|
|
78
|
-
occupancyKnownFor
|
|
79
|
-
});
|
|
80
|
-
return { offer, picture, computed: () => computed, supply };
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
test("the pool gets no positive answer while another occupied output has no measured cost", () => {
|
|
84
|
-
const { offer } = offerOver(undefined, () => false);
|
|
85
|
-
|
|
86
|
-
assert.equal(
|
|
87
|
-
offer.predictOfferedHeights({ width: 1920, height: 1080, fps: 25, bitrateKbps: 5000 }),
|
|
88
|
-
null
|
|
89
|
-
);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
test("the offer is computed once and then answered from the cache", () => {
|
|
93
|
-
// It is asked on the path that serves every playlist, every init and every
|
|
94
|
-
// segment. Recomputing it each time is the arithmetic of the whole ladder per
|
|
95
|
-
// request, and its refusal line written for the life of the film.
|
|
96
|
-
const { offer, picture, computed } = offerOver();
|
|
97
|
-
|
|
98
|
-
offer.offeredHeightsFor(picture);
|
|
99
|
-
offer.offeredHeightsFor(picture);
|
|
100
|
-
|
|
101
|
-
assert.equal(computed(), 1);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
test("what the swarm is doing is part of what identifies that cached answer", () => {
|
|
105
|
-
// The bar rises when the reader meets interruptions and the torrent's price
|
|
106
|
-
// moves every few seconds. Left out of the key, a menu computed while nothing
|
|
107
|
-
// was known about the swarm would stand for the whole film — offering steps
|
|
108
|
-
// supply cannot support, and passing every route guard on the way.
|
|
109
|
-
const supply = { requiredSpeed: null, megabytesPerSecond: null, costPerMegabyte: null };
|
|
110
|
-
const { offer, picture, computed } = offerOver(supply);
|
|
111
|
-
offer.offeredHeightsFor(picture);
|
|
112
|
-
|
|
113
|
-
supply.requiredSpeed = 6.12;
|
|
114
|
-
offer.offeredHeightsFor(picture);
|
|
115
|
-
assert.equal(computed(), 2, "the bar this file's own interruptions demand moved");
|
|
116
|
-
|
|
117
|
-
supply.megabytesPerSecond = 1.4;
|
|
118
|
-
offer.offeredHeightsFor(picture);
|
|
119
|
-
assert.equal(computed(), 3, "and so did what a viewer draws through it");
|
|
120
|
-
|
|
121
|
-
supply.costPerMegabyte = 0.05;
|
|
122
|
-
offer.offeredHeightsFor(picture);
|
|
123
|
-
assert.equal(computed(), 4, "and what a megabyte costs this process");
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
test("and so is what each running encode was last seen doing", () => {
|
|
127
|
-
// It is an input twice over — it withdraws a step measured below realtime,
|
|
128
|
-
// and it prices every running picture in the committed total. On a COPIED
|
|
129
|
-
// picture nothing else in the key ever moves, so without this the menu would
|
|
130
|
-
// be pinned to what was computed before anything had been measured.
|
|
131
|
-
const { offer, picture, computed } = offerOver();
|
|
132
|
-
offer.offeredHeightsFor(picture);
|
|
133
|
-
|
|
134
|
-
qualityStateOf(picture).lastAloneSpeed = 0.4;
|
|
135
|
-
offer.offeredHeightsFor(picture);
|
|
136
|
-
|
|
137
|
-
assert.equal(computed(), 2);
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
test("a step asking for the family's answer does not keep it as its own", () => {
|
|
141
|
-
// A step is a session of its own and knows only its own encode: asked while
|
|
142
|
-
// the viewer watches 240p, the 240p session priced the 1080p step as a
|
|
143
|
-
// re-encode — because ITS video is re-encoded — and refused it on a host that
|
|
144
|
-
// had been serving that very height by COPY minutes earlier. Only the picture
|
|
145
|
-
// knows what the family can do with the source, so the picture answers; and
|
|
146
|
-
// the answer must not then be filed on the asker, whose own flags are what
|
|
147
|
-
// made the wrong answer possible in the first place.
|
|
148
|
-
const { offer, picture } = offerOver();
|
|
149
|
-
const step = sessionProducing();
|
|
150
|
-
step.id = "9999999988887777";
|
|
151
|
-
step.spec = outputSpec({ transcodeVideo: true });
|
|
152
|
-
|
|
153
|
-
const answer = offer.offeredHeightsFor(step);
|
|
154
|
-
|
|
155
|
-
assert.ok(Array.isArray(answer), "the step is still answered — its viewer is watching it");
|
|
156
|
-
assert.equal(qualityStateOf(step).offeredHeightsCache, undefined, "and nothing is filed on the asker");
|
|
157
|
-
assert.equal(
|
|
158
|
-
qualityStateOf(picture).offeredHeightsCache,
|
|
159
|
-
undefined,
|
|
160
|
-
"nor on the picture, whose own key was never the one this answer was computed against"
|
|
161
|
-
);
|
|
162
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* @file Which heights are on the menu, and when that answer may be reused.
|
|
3
|
+
*
|
|
4
|
+
* The arithmetic is `EncodeCost` and is checked next door. What is checked here
|
|
5
|
+
* is the part that is not arithmetic: whose answer it is, what may never be
|
|
6
|
+
* withdrawn, and the cache — which is the whole reason this is an object, and
|
|
7
|
+
* which had no check of any kind while it lived in the session manager.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import test from "node:test";
|
|
11
|
+
import assert from "node:assert/strict";
|
|
12
|
+
import { EncodeCost } from "../services/encode/quality/EncodeCost.js";
|
|
13
|
+
import { QualityOffer } from "../services/encode/quality/QualityOffer.js";
|
|
14
|
+
import { startRunOn } from "./helpers/encode-run.js";
|
|
15
|
+
import { outputSpec } from "./helpers/output-spec.js";
|
|
16
|
+
import { runStateOf } from "../services/encode/encode-run-state.js";
|
|
17
|
+
import { qualityStateOf } from "../services/encode/quality/OutputQualityState.js";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A session with one run going.
|
|
21
|
+
*
|
|
22
|
+
* @returns {object}
|
|
23
|
+
*/
|
|
24
|
+
function sessionProducing() {
|
|
25
|
+
const session = {
|
|
26
|
+
id: "1111111122223333",
|
|
27
|
+
state: "ready",
|
|
28
|
+
spec: outputSpec(),
|
|
29
|
+
file: { key: "torrent:abc:0", name: "film.mkv", width: 1920, height: 1080 },
|
|
30
|
+
output: { encodeWidth: 0, encodeHeight: 0, outputFps: 25, softwarePreset: null },
|
|
31
|
+
progress: { processedSeconds: 0 },
|
|
32
|
+
runs: new Set()
|
|
33
|
+
};
|
|
34
|
+
startRunOn(session, { from: 0, speedX: 1 });
|
|
35
|
+
return session;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A picture, and the offer's cache in play.
|
|
40
|
+
*
|
|
41
|
+
* @param {object} [supply]
|
|
42
|
+
* @returns {{ offer: QualityOffer, picture: object, computed: () => number, supply: object }}
|
|
43
|
+
*/
|
|
44
|
+
function offerOver(
|
|
45
|
+
supply = { requiredSpeed: null, megabytesPerSecond: null, costPerMegabyte: null },
|
|
46
|
+
occupancyKnownFor = () => true
|
|
47
|
+
) {
|
|
48
|
+
const picture = sessionProducing();
|
|
49
|
+
const outputs = {
|
|
50
|
+
familyOf: () => [picture],
|
|
51
|
+
pictureOf: () => picture,
|
|
52
|
+
variantHeightOf: () => 0,
|
|
53
|
+
sessionsOn: () => []
|
|
54
|
+
};
|
|
55
|
+
const cost = new EncodeCost({
|
|
56
|
+
outputs,
|
|
57
|
+
host: () => ({ benchmark: null, decodeModel: null, contentionPenalties: null, availability: null }),
|
|
58
|
+
runningEncoders: () => 0,
|
|
59
|
+
encodersRunningNow: () => 0,
|
|
60
|
+
torrentCostSecFor: () => 0,
|
|
61
|
+
boundBy: async () => "cpu",
|
|
62
|
+
runsFor: (session) => [...(session.runs ?? [])],
|
|
63
|
+
stateFor: (session) => runStateOf(session.runs),
|
|
64
|
+
progressFor: (session) => session.progress ?? null
|
|
65
|
+
});
|
|
66
|
+
let computed = 0;
|
|
67
|
+
const real = cost.sustainableHeights.bind(cost);
|
|
68
|
+
cost.sustainableHeights = (params) => {
|
|
69
|
+
computed += 1;
|
|
70
|
+
return real(params);
|
|
71
|
+
};
|
|
72
|
+
const offer = new QualityOffer({
|
|
73
|
+
encodeCost: cost,
|
|
74
|
+
outputs,
|
|
75
|
+
stateFor: (session) => runStateOf(session.runs),
|
|
76
|
+
heightsOnScreen: () => [],
|
|
77
|
+
supplyFor: () => supply,
|
|
78
|
+
occupancyKnownFor
|
|
79
|
+
});
|
|
80
|
+
return { offer, picture, computed: () => computed, supply };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
test("the pool gets no positive answer while another occupied output has no measured cost", () => {
|
|
84
|
+
const { offer } = offerOver(undefined, () => false);
|
|
85
|
+
|
|
86
|
+
assert.equal(
|
|
87
|
+
offer.predictOfferedHeights({ width: 1920, height: 1080, fps: 25, bitrateKbps: 5000 }),
|
|
88
|
+
null
|
|
89
|
+
);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test("the offer is computed once and then answered from the cache", () => {
|
|
93
|
+
// It is asked on the path that serves every playlist, every init and every
|
|
94
|
+
// segment. Recomputing it each time is the arithmetic of the whole ladder per
|
|
95
|
+
// request, and its refusal line written for the life of the film.
|
|
96
|
+
const { offer, picture, computed } = offerOver();
|
|
97
|
+
|
|
98
|
+
offer.offeredHeightsFor(picture);
|
|
99
|
+
offer.offeredHeightsFor(picture);
|
|
100
|
+
|
|
101
|
+
assert.equal(computed(), 1);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("what the swarm is doing is part of what identifies that cached answer", () => {
|
|
105
|
+
// The bar rises when the reader meets interruptions and the torrent's price
|
|
106
|
+
// moves every few seconds. Left out of the key, a menu computed while nothing
|
|
107
|
+
// was known about the swarm would stand for the whole film — offering steps
|
|
108
|
+
// supply cannot support, and passing every route guard on the way.
|
|
109
|
+
const supply = { requiredSpeed: null, megabytesPerSecond: null, costPerMegabyte: null };
|
|
110
|
+
const { offer, picture, computed } = offerOver(supply);
|
|
111
|
+
offer.offeredHeightsFor(picture);
|
|
112
|
+
|
|
113
|
+
supply.requiredSpeed = 6.12;
|
|
114
|
+
offer.offeredHeightsFor(picture);
|
|
115
|
+
assert.equal(computed(), 2, "the bar this file's own interruptions demand moved");
|
|
116
|
+
|
|
117
|
+
supply.megabytesPerSecond = 1.4;
|
|
118
|
+
offer.offeredHeightsFor(picture);
|
|
119
|
+
assert.equal(computed(), 3, "and so did what a viewer draws through it");
|
|
120
|
+
|
|
121
|
+
supply.costPerMegabyte = 0.05;
|
|
122
|
+
offer.offeredHeightsFor(picture);
|
|
123
|
+
assert.equal(computed(), 4, "and what a megabyte costs this process");
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("and so is what each running encode was last seen doing", () => {
|
|
127
|
+
// It is an input twice over — it withdraws a step measured below realtime,
|
|
128
|
+
// and it prices every running picture in the committed total. On a COPIED
|
|
129
|
+
// picture nothing else in the key ever moves, so without this the menu would
|
|
130
|
+
// be pinned to what was computed before anything had been measured.
|
|
131
|
+
const { offer, picture, computed } = offerOver();
|
|
132
|
+
offer.offeredHeightsFor(picture);
|
|
133
|
+
|
|
134
|
+
qualityStateOf(picture).lastAloneSpeed = 0.4;
|
|
135
|
+
offer.offeredHeightsFor(picture);
|
|
136
|
+
|
|
137
|
+
assert.equal(computed(), 2);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test("a step asking for the family's answer does not keep it as its own", () => {
|
|
141
|
+
// A step is a session of its own and knows only its own encode: asked while
|
|
142
|
+
// the viewer watches 240p, the 240p session priced the 1080p step as a
|
|
143
|
+
// re-encode — because ITS video is re-encoded — and refused it on a host that
|
|
144
|
+
// had been serving that very height by COPY minutes earlier. Only the picture
|
|
145
|
+
// knows what the family can do with the source, so the picture answers; and
|
|
146
|
+
// the answer must not then be filed on the asker, whose own flags are what
|
|
147
|
+
// made the wrong answer possible in the first place.
|
|
148
|
+
const { offer, picture } = offerOver();
|
|
149
|
+
const step = sessionProducing();
|
|
150
|
+
step.id = "9999999988887777";
|
|
151
|
+
step.spec = outputSpec({ transcodeVideo: true });
|
|
152
|
+
|
|
153
|
+
const answer = offer.offeredHeightsFor(step);
|
|
154
|
+
|
|
155
|
+
assert.ok(Array.isArray(answer), "the step is still answered — its viewer is watching it");
|
|
156
|
+
assert.equal(qualityStateOf(step).offeredHeightsCache, undefined, "and nothing is filed on the asker");
|
|
157
|
+
assert.equal(
|
|
158
|
+
qualityStateOf(picture).offeredHeightsCache,
|
|
159
|
+
undefined,
|
|
160
|
+
"nor on the picture, whose own key was never the one this answer was computed against"
|
|
161
|
+
);
|
|
162
|
+
});
|