@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
package/test/video-facts.test.js
CHANGED
|
@@ -1,102 +1,102 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file The picture's facts, from the two readings that state them.
|
|
3
|
-
*
|
|
4
|
-
* Audio and subtitles have had this reconciliation since their flags were first
|
|
5
|
-
* read from the file. Video never did: every figure the encode is planned from
|
|
6
|
-
* came from ffmpeg's `-i` banner alone, and the `VideoTrack` the container
|
|
7
|
-
* declares was read and then used for nothing but a line in the log — so a file
|
|
8
|
-
* that states its bit depth or its HDR signalling, against a probe that does
|
|
9
|
-
* not print them, disagreed with nobody watching.
|
|
10
|
-
*
|
|
11
|
-
* What each check pins is WHICH reading answers, and why: the size and the
|
|
12
|
-
* frame rate are what the decoder will produce, so the probe answers; the bit
|
|
13
|
-
* depth and the HDR signalling are the file saying how its samples are to be
|
|
14
|
-
* read, so the container answers.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import test from "node:test";
|
|
18
|
-
import assert from "node:assert/strict";
|
|
19
|
-
|
|
20
|
-
import { Container } from "../services/media/container/Container.js";
|
|
21
|
-
|
|
22
|
-
const banner = (fields) => ({ width: null, height: null, fps: null, isHdr: false, bitDepth: null, ...fields });
|
|
23
|
-
|
|
24
|
-
test("with no container reading the probe answers for everything", () => {
|
|
25
|
-
const facts = Container.mergeVideoFacts(
|
|
26
|
-
banner({ width: 1920, height: 1080, fps: 23.976, isHdr: true, bitDepth: 10 }),
|
|
27
|
-
null
|
|
28
|
-
);
|
|
29
|
-
assert.equal(facts.width, 1920);
|
|
30
|
-
assert.equal(facts.height, 1080);
|
|
31
|
-
assert.equal(facts.fps, 23.976);
|
|
32
|
-
assert.equal(facts.isHdr, true);
|
|
33
|
-
assert.equal(facts.bitDepth, 10);
|
|
34
|
-
assert.deepEqual(facts.disagreements, []);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
test("the size and the frame rate are the probe's, because that is what will be decoded", () => {
|
|
38
|
-
const facts = Container.mergeVideoFacts(
|
|
39
|
-
banner({ width: 1920, height: 1080, fps: 24 }),
|
|
40
|
-
{ width: 1440, height: 1080, fps: 25, displayWidth: 1920, displayHeight: 1080 }
|
|
41
|
-
);
|
|
42
|
-
assert.equal(facts.width, 1920, "the ladder and the scale filter are sized to the decoded frame");
|
|
43
|
-
assert.equal(facts.height, 1080);
|
|
44
|
-
assert.equal(facts.fps, 24);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
test("the bit depth and the HDR signalling are the file's, because the file states them", () => {
|
|
48
|
-
// A ten-bit HEVC whose probe printed neither: this is the case that decides
|
|
49
|
-
// whether tone mapping runs and how the decode cost is priced.
|
|
50
|
-
const facts = Container.mergeVideoFacts(
|
|
51
|
-
banner({ width: 3840, height: 2160, fps: 24 }),
|
|
52
|
-
{ width: 3840, height: 2160, bitDepth: 10, isHdr: true }
|
|
53
|
-
);
|
|
54
|
-
assert.equal(facts.bitDepth, 10);
|
|
55
|
-
assert.equal(facts.isHdr, true);
|
|
56
|
-
assert.deepEqual(facts.disagreements, [], "one side saying nothing is not a disagreement");
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
test("a field only the probe states is still answered", () => {
|
|
60
|
-
const facts = Container.mergeVideoFacts(
|
|
61
|
-
banner({ width: 1280, height: 720, fps: 30, bitDepth: 8 }),
|
|
62
|
-
{ width: null, height: null, fps: null, bitDepth: null }
|
|
63
|
-
);
|
|
64
|
-
assert.equal(facts.width, 1280);
|
|
65
|
-
assert.equal(facts.bitDepth, 8);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test("a real disagreement is reported, with both values and which is which", () => {
|
|
69
|
-
const facts = Container.mergeVideoFacts(
|
|
70
|
-
banner({ width: 1920, height: 1080, bitDepth: 8, isHdr: false }),
|
|
71
|
-
{ width: 1280, height: 720, bitDepth: 10, isHdr: true }
|
|
72
|
-
);
|
|
73
|
-
assert.equal(facts.disagreements.length, 3);
|
|
74
|
-
assert.ok(facts.disagreements.some((line) => /width 1280 in the container against 1920 in the probe/.test(line)));
|
|
75
|
-
assert.ok(facts.disagreements.some((line) => /bit depth 10 in the container against 8 in the probe/.test(line)));
|
|
76
|
-
// HDR is not among them, and cannot be: see `mergeVideoFacts`.
|
|
77
|
-
assert.ok(!facts.disagreements.some((line) => /HDR/.test(line)));
|
|
78
|
-
// And the rule still decides: the probe for the size, the file for the rest.
|
|
79
|
-
assert.equal(facts.width, 1920);
|
|
80
|
-
assert.equal(facts.bitDepth, 10);
|
|
81
|
-
assert.equal(facts.isHdr, true);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
test("the display size is the container's alone — the probe has no such field", () => {
|
|
85
|
-
const facts = Container.mergeVideoFacts(
|
|
86
|
-
banner({ width: 1440, height: 1080 }),
|
|
87
|
-
{ width: 1440, height: 1080, displayWidth: 1920, displayHeight: 1080 }
|
|
88
|
-
);
|
|
89
|
-
assert.equal(facts.displayWidth, 1920);
|
|
90
|
-
assert.equal(facts.displayHeight, 1080);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
test("zero and nonsense are not values", () => {
|
|
94
|
-
const facts = Container.mergeVideoFacts(
|
|
95
|
-
banner({ width: 0, height: 0, fps: 0, bitDepth: 0 }),
|
|
96
|
-
{ width: 1920, height: 1080, fps: 24, bitDepth: 8 }
|
|
97
|
-
);
|
|
98
|
-
assert.equal(facts.width, 1920, "a probe that printed nothing does not outrank a file that speaks");
|
|
99
|
-
assert.equal(facts.fps, 24);
|
|
100
|
-
assert.equal(facts.bitDepth, 8);
|
|
101
|
-
assert.deepEqual(facts.disagreements, []);
|
|
102
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* @file The picture's facts, from the two readings that state them.
|
|
3
|
+
*
|
|
4
|
+
* Audio and subtitles have had this reconciliation since their flags were first
|
|
5
|
+
* read from the file. Video never did: every figure the encode is planned from
|
|
6
|
+
* came from ffmpeg's `-i` banner alone, and the `VideoTrack` the container
|
|
7
|
+
* declares was read and then used for nothing but a line in the log — so a file
|
|
8
|
+
* that states its bit depth or its HDR signalling, against a probe that does
|
|
9
|
+
* not print them, disagreed with nobody watching.
|
|
10
|
+
*
|
|
11
|
+
* What each check pins is WHICH reading answers, and why: the size and the
|
|
12
|
+
* frame rate are what the decoder will produce, so the probe answers; the bit
|
|
13
|
+
* depth and the HDR signalling are the file saying how its samples are to be
|
|
14
|
+
* read, so the container answers.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import test from "node:test";
|
|
18
|
+
import assert from "node:assert/strict";
|
|
19
|
+
|
|
20
|
+
import { Container } from "../services/media/container/Container.js";
|
|
21
|
+
|
|
22
|
+
const banner = (fields) => ({ width: null, height: null, fps: null, isHdr: false, bitDepth: null, ...fields });
|
|
23
|
+
|
|
24
|
+
test("with no container reading the probe answers for everything", () => {
|
|
25
|
+
const facts = Container.mergeVideoFacts(
|
|
26
|
+
banner({ width: 1920, height: 1080, fps: 23.976, isHdr: true, bitDepth: 10 }),
|
|
27
|
+
null
|
|
28
|
+
);
|
|
29
|
+
assert.equal(facts.width, 1920);
|
|
30
|
+
assert.equal(facts.height, 1080);
|
|
31
|
+
assert.equal(facts.fps, 23.976);
|
|
32
|
+
assert.equal(facts.isHdr, true);
|
|
33
|
+
assert.equal(facts.bitDepth, 10);
|
|
34
|
+
assert.deepEqual(facts.disagreements, []);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("the size and the frame rate are the probe's, because that is what will be decoded", () => {
|
|
38
|
+
const facts = Container.mergeVideoFacts(
|
|
39
|
+
banner({ width: 1920, height: 1080, fps: 24 }),
|
|
40
|
+
{ width: 1440, height: 1080, fps: 25, displayWidth: 1920, displayHeight: 1080 }
|
|
41
|
+
);
|
|
42
|
+
assert.equal(facts.width, 1920, "the ladder and the scale filter are sized to the decoded frame");
|
|
43
|
+
assert.equal(facts.height, 1080);
|
|
44
|
+
assert.equal(facts.fps, 24);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("the bit depth and the HDR signalling are the file's, because the file states them", () => {
|
|
48
|
+
// A ten-bit HEVC whose probe printed neither: this is the case that decides
|
|
49
|
+
// whether tone mapping runs and how the decode cost is priced.
|
|
50
|
+
const facts = Container.mergeVideoFacts(
|
|
51
|
+
banner({ width: 3840, height: 2160, fps: 24 }),
|
|
52
|
+
{ width: 3840, height: 2160, bitDepth: 10, isHdr: true }
|
|
53
|
+
);
|
|
54
|
+
assert.equal(facts.bitDepth, 10);
|
|
55
|
+
assert.equal(facts.isHdr, true);
|
|
56
|
+
assert.deepEqual(facts.disagreements, [], "one side saying nothing is not a disagreement");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test("a field only the probe states is still answered", () => {
|
|
60
|
+
const facts = Container.mergeVideoFacts(
|
|
61
|
+
banner({ width: 1280, height: 720, fps: 30, bitDepth: 8 }),
|
|
62
|
+
{ width: null, height: null, fps: null, bitDepth: null }
|
|
63
|
+
);
|
|
64
|
+
assert.equal(facts.width, 1280);
|
|
65
|
+
assert.equal(facts.bitDepth, 8);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("a real disagreement is reported, with both values and which is which", () => {
|
|
69
|
+
const facts = Container.mergeVideoFacts(
|
|
70
|
+
banner({ width: 1920, height: 1080, bitDepth: 8, isHdr: false }),
|
|
71
|
+
{ width: 1280, height: 720, bitDepth: 10, isHdr: true }
|
|
72
|
+
);
|
|
73
|
+
assert.equal(facts.disagreements.length, 3);
|
|
74
|
+
assert.ok(facts.disagreements.some((line) => /width 1280 in the container against 1920 in the probe/.test(line)));
|
|
75
|
+
assert.ok(facts.disagreements.some((line) => /bit depth 10 in the container against 8 in the probe/.test(line)));
|
|
76
|
+
// HDR is not among them, and cannot be: see `mergeVideoFacts`.
|
|
77
|
+
assert.ok(!facts.disagreements.some((line) => /HDR/.test(line)));
|
|
78
|
+
// And the rule still decides: the probe for the size, the file for the rest.
|
|
79
|
+
assert.equal(facts.width, 1920);
|
|
80
|
+
assert.equal(facts.bitDepth, 10);
|
|
81
|
+
assert.equal(facts.isHdr, true);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("the display size is the container's alone — the probe has no such field", () => {
|
|
85
|
+
const facts = Container.mergeVideoFacts(
|
|
86
|
+
banner({ width: 1440, height: 1080 }),
|
|
87
|
+
{ width: 1440, height: 1080, displayWidth: 1920, displayHeight: 1080 }
|
|
88
|
+
);
|
|
89
|
+
assert.equal(facts.displayWidth, 1920);
|
|
90
|
+
assert.equal(facts.displayHeight, 1080);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("zero and nonsense are not values", () => {
|
|
94
|
+
const facts = Container.mergeVideoFacts(
|
|
95
|
+
banner({ width: 0, height: 0, fps: 0, bitDepth: 0 }),
|
|
96
|
+
{ width: 1920, height: 1080, fps: 24, bitDepth: 8 }
|
|
97
|
+
);
|
|
98
|
+
assert.equal(facts.width, 1920, "a probe that printed nothing does not outrank a file that speaks");
|
|
99
|
+
assert.equal(facts.fps, 24);
|
|
100
|
+
assert.equal(facts.bitDepth, 8);
|
|
101
|
+
assert.deepEqual(facts.disagreements, []);
|
|
102
|
+
});
|
|
@@ -1,143 +1,143 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file What a viewer's own assignment to an output must do.
|
|
3
|
-
*
|
|
4
|
-
* The property under all of it: one viewer's link, seek or choice never moves
|
|
5
|
-
* another viewer's output. That is roadmap item 97, and the record it replaces
|
|
6
|
-
* — one entry per (file, height) for everybody — could not express it.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import test from "node:test";
|
|
10
|
-
import assert from "node:assert/strict";
|
|
11
|
-
import { Assignments } from "../services/viewer/Assignments.js";
|
|
12
|
-
|
|
13
|
-
const WINDOW_MS = 30_000;
|
|
14
|
-
const make = () => new Assignments({ acceptWindowMs: WINDOW_MS });
|
|
15
|
-
|
|
16
|
-
test("the acceptance window is given, not invented", () => {
|
|
17
|
-
assert.throws(() => new Assignments({}), TypeError);
|
|
18
|
-
assert.throws(() => new Assignments({ acceptWindowMs: -1 }), TypeError);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
test("a choice stands until the rule moves it, and a request never moves it", () => {
|
|
22
|
-
const mine = make();
|
|
23
|
-
|
|
24
|
-
mine.choose(720, "out-a");
|
|
25
|
-
mine.give(mine.generation, 720, 4, "out-a");
|
|
26
|
-
|
|
27
|
-
assert.equal(mine.chosenFor(720), "out-a");
|
|
28
|
-
mine.give(mine.generation, 720, 5, "out-b");
|
|
29
|
-
assert.equal(mine.chosenFor(720), "out-a", "being given something else does not re-choose");
|
|
30
|
-
mine.choose(720, "out-b");
|
|
31
|
-
assert.equal(mine.chosenFor(720), "out-b");
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
test("two viewers do not share an answer", () => {
|
|
35
|
-
const thin = make();
|
|
36
|
-
const thick = make();
|
|
37
|
-
|
|
38
|
-
thin.choose(720, "out-540-slow");
|
|
39
|
-
thick.choose(720, "out-720-full");
|
|
40
|
-
|
|
41
|
-
assert.equal(thin.chosenFor(720), "out-540-slow");
|
|
42
|
-
assert.equal(thick.chosenFor(720), "out-720-full", "the other viewer's link decided nothing here");
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
test("a repeat of a request is answered by whatever answered it, not decided again", () => {
|
|
46
|
-
const mine = make();
|
|
47
|
-
const first = mine.generation;
|
|
48
|
-
|
|
49
|
-
mine.give(first, 720, 9, "out-a");
|
|
50
|
-
|
|
51
|
-
assert.equal(mine.givenFor(first, 720, 9), "out-a");
|
|
52
|
-
assert.equal(mine.givenFor(first, 720, 10), "", "a segment nothing answered says so");
|
|
53
|
-
assert.equal(mine.givenFor(first + 1, 720, 9), "", "and a later generation is not the same question");
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
test("a request carries its own generation, so a late one is not read against the current", () => {
|
|
57
|
-
const mine = make();
|
|
58
|
-
const made = mine.generation;
|
|
59
|
-
|
|
60
|
-
mine.statedGeneration(1, 1_000);
|
|
61
|
-
|
|
62
|
-
assert.equal(mine.generation, 1);
|
|
63
|
-
assert.notEqual(mine.generation, made);
|
|
64
|
-
assert.equal(mine.accepts(made, 1_000 + WINDOW_MS), true, "inside the window it is still answered");
|
|
65
|
-
assert.equal(mine.accepts(made, 1_000 + WINDOW_MS + 1), false, "past it, a new request is not");
|
|
66
|
-
assert.equal(mine.accepts(mine.generation, 1_000 + 10 * WINDOW_MS), true, "the current one never runs out");
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
test("an accepted response keeps its output past its own generation's window", () => {
|
|
70
|
-
const mine = make();
|
|
71
|
-
const made = mine.generation;
|
|
72
|
-
mine.give(made, 720, 3, "out-a");
|
|
73
|
-
const token = mine.accept("out-a");
|
|
74
|
-
|
|
75
|
-
mine.statedGeneration(1, 1_000);
|
|
76
|
-
mine.choose(720, "out-b");
|
|
77
|
-
|
|
78
|
-
const longAfter = 1_000 + 10 * WINDOW_MS;
|
|
79
|
-
assert.equal(mine.accepts(made, longAfter), false, "no NEW request of that generation is taken");
|
|
80
|
-
assert.ok(
|
|
81
|
-
mine.heldKeys(longAfter).has("out-a"),
|
|
82
|
-
"but the output whose bytes are still going out is held — dropping it is what the second deadline exists to prevent"
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
mine.release(token);
|
|
86
|
-
assert.equal(mine.heldKeys(longAfter).has("out-a"), false, "and released once the response is done");
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
test("what was given inside a live generation holds its output, and is forgotten past the window", () => {
|
|
90
|
-
const mine = make();
|
|
91
|
-
const made = mine.generation;
|
|
92
|
-
mine.give(made, 720, 3, "out-a");
|
|
93
|
-
mine.statedGeneration(1, 1_000);
|
|
94
|
-
|
|
95
|
-
assert.ok(mine.heldKeys(1_000 + WINDOW_MS).has("out-a"));
|
|
96
|
-
assert.equal(
|
|
97
|
-
mine.heldKeys(1_000 + WINDOW_MS + 1).has("out-a"),
|
|
98
|
-
false,
|
|
99
|
-
"past the window nothing stands on it, so it may be let go"
|
|
100
|
-
);
|
|
101
|
-
assert.equal(
|
|
102
|
-
mine.givenFor(made, 720, 3),
|
|
103
|
-
"",
|
|
104
|
-
"and the generation is pruned rather than kept for the life of the viewing"
|
|
105
|
-
);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
test("a viewer leaving holds nothing, response in flight included", () => {
|
|
109
|
-
const mine = make();
|
|
110
|
-
mine.choose(720, "out-a");
|
|
111
|
-
mine.accept("out-b");
|
|
112
|
-
mine.give(mine.generation, 720, 1, "out-c");
|
|
113
|
-
|
|
114
|
-
mine.clear();
|
|
115
|
-
|
|
116
|
-
assert.equal(mine.heldKeys().size, 0);
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
test("the page owns the number, so a lower one does not take the viewer back", () => {
|
|
120
|
-
const mine = make();
|
|
121
|
-
mine.statedGeneration(4, 1_000);
|
|
122
|
-
|
|
123
|
-
assert.equal(mine.statedGeneration(2, 2_000), 4, "a reordered or repeated message changes nothing");
|
|
124
|
-
assert.equal(mine.statedGeneration(4, 2_000), 4, "and neither does the one already current");
|
|
125
|
-
assert.equal(mine.generation, 4);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
test("a request ahead of what the page has announced is still answered", () => {
|
|
129
|
-
const mine = make();
|
|
130
|
-
|
|
131
|
-
// The page raises its own number before it sends anything, so a segment
|
|
132
|
-
// request can overtake the seek that announces the new viewing.
|
|
133
|
-
assert.equal(mine.accepts(1), true, "being behind the page means this side has not heard yet");
|
|
134
|
-
assert.equal(mine.accepts(7), true);
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
test("a request that states no generation is answered", () => {
|
|
138
|
-
const mine = make();
|
|
139
|
-
mine.statedGeneration(3, 1_000);
|
|
140
|
-
|
|
141
|
-
assert.equal(mine.accepts(Number.NaN, 1_000 + 10 * WINDOW_MS), true);
|
|
142
|
-
assert.equal(mine.accepts(undefined, 1_000 + 10 * WINDOW_MS), true, "a transport with no loader of ours builds no such parameter");
|
|
143
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* @file What a viewer's own assignment to an output must do.
|
|
3
|
+
*
|
|
4
|
+
* The property under all of it: one viewer's link, seek or choice never moves
|
|
5
|
+
* another viewer's output. That is roadmap item 97, and the record it replaces
|
|
6
|
+
* — one entry per (file, height) for everybody — could not express it.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import test from "node:test";
|
|
10
|
+
import assert from "node:assert/strict";
|
|
11
|
+
import { Assignments } from "../services/viewer/Assignments.js";
|
|
12
|
+
|
|
13
|
+
const WINDOW_MS = 30_000;
|
|
14
|
+
const make = () => new Assignments({ acceptWindowMs: WINDOW_MS });
|
|
15
|
+
|
|
16
|
+
test("the acceptance window is given, not invented", () => {
|
|
17
|
+
assert.throws(() => new Assignments({}), TypeError);
|
|
18
|
+
assert.throws(() => new Assignments({ acceptWindowMs: -1 }), TypeError);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("a choice stands until the rule moves it, and a request never moves it", () => {
|
|
22
|
+
const mine = make();
|
|
23
|
+
|
|
24
|
+
mine.choose(720, "out-a");
|
|
25
|
+
mine.give(mine.generation, 720, 4, "out-a");
|
|
26
|
+
|
|
27
|
+
assert.equal(mine.chosenFor(720), "out-a");
|
|
28
|
+
mine.give(mine.generation, 720, 5, "out-b");
|
|
29
|
+
assert.equal(mine.chosenFor(720), "out-a", "being given something else does not re-choose");
|
|
30
|
+
mine.choose(720, "out-b");
|
|
31
|
+
assert.equal(mine.chosenFor(720), "out-b");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("two viewers do not share an answer", () => {
|
|
35
|
+
const thin = make();
|
|
36
|
+
const thick = make();
|
|
37
|
+
|
|
38
|
+
thin.choose(720, "out-540-slow");
|
|
39
|
+
thick.choose(720, "out-720-full");
|
|
40
|
+
|
|
41
|
+
assert.equal(thin.chosenFor(720), "out-540-slow");
|
|
42
|
+
assert.equal(thick.chosenFor(720), "out-720-full", "the other viewer's link decided nothing here");
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("a repeat of a request is answered by whatever answered it, not decided again", () => {
|
|
46
|
+
const mine = make();
|
|
47
|
+
const first = mine.generation;
|
|
48
|
+
|
|
49
|
+
mine.give(first, 720, 9, "out-a");
|
|
50
|
+
|
|
51
|
+
assert.equal(mine.givenFor(first, 720, 9), "out-a");
|
|
52
|
+
assert.equal(mine.givenFor(first, 720, 10), "", "a segment nothing answered says so");
|
|
53
|
+
assert.equal(mine.givenFor(first + 1, 720, 9), "", "and a later generation is not the same question");
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("a request carries its own generation, so a late one is not read against the current", () => {
|
|
57
|
+
const mine = make();
|
|
58
|
+
const made = mine.generation;
|
|
59
|
+
|
|
60
|
+
mine.statedGeneration(1, 1_000);
|
|
61
|
+
|
|
62
|
+
assert.equal(mine.generation, 1);
|
|
63
|
+
assert.notEqual(mine.generation, made);
|
|
64
|
+
assert.equal(mine.accepts(made, 1_000 + WINDOW_MS), true, "inside the window it is still answered");
|
|
65
|
+
assert.equal(mine.accepts(made, 1_000 + WINDOW_MS + 1), false, "past it, a new request is not");
|
|
66
|
+
assert.equal(mine.accepts(mine.generation, 1_000 + 10 * WINDOW_MS), true, "the current one never runs out");
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("an accepted response keeps its output past its own generation's window", () => {
|
|
70
|
+
const mine = make();
|
|
71
|
+
const made = mine.generation;
|
|
72
|
+
mine.give(made, 720, 3, "out-a");
|
|
73
|
+
const token = mine.accept("out-a");
|
|
74
|
+
|
|
75
|
+
mine.statedGeneration(1, 1_000);
|
|
76
|
+
mine.choose(720, "out-b");
|
|
77
|
+
|
|
78
|
+
const longAfter = 1_000 + 10 * WINDOW_MS;
|
|
79
|
+
assert.equal(mine.accepts(made, longAfter), false, "no NEW request of that generation is taken");
|
|
80
|
+
assert.ok(
|
|
81
|
+
mine.heldKeys(longAfter).has("out-a"),
|
|
82
|
+
"but the output whose bytes are still going out is held — dropping it is what the second deadline exists to prevent"
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
mine.release(token);
|
|
86
|
+
assert.equal(mine.heldKeys(longAfter).has("out-a"), false, "and released once the response is done");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("what was given inside a live generation holds its output, and is forgotten past the window", () => {
|
|
90
|
+
const mine = make();
|
|
91
|
+
const made = mine.generation;
|
|
92
|
+
mine.give(made, 720, 3, "out-a");
|
|
93
|
+
mine.statedGeneration(1, 1_000);
|
|
94
|
+
|
|
95
|
+
assert.ok(mine.heldKeys(1_000 + WINDOW_MS).has("out-a"));
|
|
96
|
+
assert.equal(
|
|
97
|
+
mine.heldKeys(1_000 + WINDOW_MS + 1).has("out-a"),
|
|
98
|
+
false,
|
|
99
|
+
"past the window nothing stands on it, so it may be let go"
|
|
100
|
+
);
|
|
101
|
+
assert.equal(
|
|
102
|
+
mine.givenFor(made, 720, 3),
|
|
103
|
+
"",
|
|
104
|
+
"and the generation is pruned rather than kept for the life of the viewing"
|
|
105
|
+
);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("a viewer leaving holds nothing, response in flight included", () => {
|
|
109
|
+
const mine = make();
|
|
110
|
+
mine.choose(720, "out-a");
|
|
111
|
+
mine.accept("out-b");
|
|
112
|
+
mine.give(mine.generation, 720, 1, "out-c");
|
|
113
|
+
|
|
114
|
+
mine.clear();
|
|
115
|
+
|
|
116
|
+
assert.equal(mine.heldKeys().size, 0);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("the page owns the number, so a lower one does not take the viewer back", () => {
|
|
120
|
+
const mine = make();
|
|
121
|
+
mine.statedGeneration(4, 1_000);
|
|
122
|
+
|
|
123
|
+
assert.equal(mine.statedGeneration(2, 2_000), 4, "a reordered or repeated message changes nothing");
|
|
124
|
+
assert.equal(mine.statedGeneration(4, 2_000), 4, "and neither does the one already current");
|
|
125
|
+
assert.equal(mine.generation, 4);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("a request ahead of what the page has announced is still answered", () => {
|
|
129
|
+
const mine = make();
|
|
130
|
+
|
|
131
|
+
// The page raises its own number before it sends anything, so a segment
|
|
132
|
+
// request can overtake the seek that announces the new viewing.
|
|
133
|
+
assert.equal(mine.accepts(1), true, "being behind the page means this side has not heard yet");
|
|
134
|
+
assert.equal(mine.accepts(7), true);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("a request that states no generation is answered", () => {
|
|
138
|
+
const mine = make();
|
|
139
|
+
mine.statedGeneration(3, 1_000);
|
|
140
|
+
|
|
141
|
+
assert.equal(mine.accepts(Number.NaN, 1_000 + 10 * WINDOW_MS), true);
|
|
142
|
+
assert.equal(mine.accepts(undefined, 1_000 + 10 * WINDOW_MS), true, "a transport with no loader of ours builds no such parameter");
|
|
143
|
+
});
|