@torrent-tv/proxy 2.89.7 → 2.89.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 +358 -338
- package/CHANGELOG.md +2340 -2335
- package/Dockerfile +30 -30
- package/LICENSE +16 -16
- package/README.md +369 -369
- package/assets/calibration/NOTICE.md +100 -100
- package/assets/calibration/audio/NOTICE.md +19 -19
- package/assets/calibration/audio/manifest.json +98 -98
- package/assets/diagnostics/sctpstate.gdb +31 -31
- package/bin/cli.js +684 -684
- package/bin/thread-pool.js +30 -30
- package/biome.json +412 -412
- package/docs/browser-proxy-contract.md +155 -155
- package/docs/container-architecture.md +273 -273
- package/docs/download-architecture.md +261 -261
- package/docs/encode-architecture.md +765 -765
- package/docs/encode-run-state.md +96 -96
- package/docs/logs.md +97 -97
- package/docs/playback-start-forecast.md +113 -113
- package/docs/socks5-egress-research.md +114 -114
- package/docs/storage-architecture.md +273 -273
- 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 +51 -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/audio-tracks/post.js +29 -29
- package/routes/api/playback-plan/post.js +53 -53
- package/routes/api/sources/files/get.js +106 -106
- 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 -217
- 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 +138 -138
- package/routes/api/transcode-sessions/post.js +178 -178
- 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 +416 -416
- package/routes/transcode/audio-file/get.js +64 -64
- package/routes/transcode/audio-warm/get.js +95 -95
- 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/build-audio-samples.mjs +31 -31
- package/scripts/render-run-graph.js +173 -173
- package/server.js +748 -748
- package/services/encode/CoverageMap.js +428 -428
- package/services/encode/CushionReport.js +345 -345
- package/services/encode/EncodeAdmission.js +376 -376
- package/services/encode/EncodeOrchestrator.js +1192 -1192
- package/services/encode/EncodePlan.js +1368 -1368
- package/services/encode/EncodeRun.js +36 -12
- 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 +784 -784
- package/services/encode/OutputTimes.js +579 -579
- package/services/encode/QsvEncoder.js +58 -58
- package/services/encode/Renditions.js +2283 -2283
- package/services/encode/RunClock.js +87 -87
- package/services/encode/RunProgress.js +79 -79
- 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 +418 -418
- package/services/encode/audio-calibration.js +67 -67
- package/services/encode/audio-work-rate.js +15 -15
- 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 +116 -116
- package/services/encode/encode-run-state.js +551 -551
- package/services/encode/encoder-readings.js +43 -43
- 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 +24 -24
- 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 +1043 -1043
- package/services/encode/quality/HostLoad.js +525 -525
- package/services/encode/quality/HostTimings.js +249 -249
- package/services/encode/quality/OutputQualityState.js +24 -24
- package/services/encode/quality/QualityController.js +758 -758
- package/services/encode/quality/QualityOffer.js +416 -416
- package/services/encode/quality/drain-threat.js +47 -47
- package/services/encode/quality/link-budget.js +337 -337
- package/services/encode/quality/output-format.js +319 -319
- 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 +757 -757
- package/services/encode/run-costs.js +155 -155
- package/services/encode/run-inputs.js +53 -53
- package/services/encode/segment-formats/fmp4.js +476 -476
- package/services/encode/segment-formats/init-compat.js +324 -324
- 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 +202 -202
- package/services/media/KeyframeTables.js +356 -356
- package/services/media/SourceFile.js +298 -298
- package/services/media/SubtitleCues.js +592 -592
- package/services/media/SubtitleOrchestrator.js +167 -167
- package/services/media/audio-inventory.js +299 -299
- package/services/media/container/AviContainer.js +268 -268
- package/services/media/container/Container.js +632 -632
- package/services/media/container/ContainerFactory.js +38 -38
- package/services/media/container/KeyframeTable.js +272 -272
- package/services/media/container/MatroskaContainer.js +1031 -1031
- package/services/media/container/Mp4Container.js +1354 -1354
- package/services/media/container/SubtitleFileContainer.js +339 -339
- package/services/media/container/ebml-reader.js +159 -159
- package/services/media/container/ebml-stream.js +174 -174
- package/services/media/container/matroska-clusters.js +1165 -1165
- package/services/media/container/unavailable.js +77 -77
- 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 +931 -931
- package/services/media/tracks/AudioTrack.js +143 -143
- package/services/media/tracks/ContainerTrack.js +84 -84
- package/services/media/tracks/ImageSubtitleTrack.js +19 -19
- package/services/media/tracks/SubtitleTrack.js +38 -38
- package/services/media/tracks/TextSubtitleTrack.js +295 -295
- package/services/media/tracks/VideoTrack.js +33 -33
- package/services/media/tracks/audio-bound.js +233 -233
- 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 +1152 -1152
- package/services/server/ViewerRequests.js +785 -785
- package/services/server/WaitLedger.js +142 -142
- package/services/server/controllers/PlaybackController.js +40 -40
- 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 +1029 -1029
- 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 +539 -539
- package/services/storage/piece-store/piece-lru.js +495 -495
- package/services/storage/piece-store/shared-piece-store.js +1937 -1937
- package/services/storage/returns.js +103 -103
- package/services/storage/segment-store/SegmentStore.js +1294 -1294
- package/services/storage/wire.js +175 -175
- package/services/torrent/Contents.js +382 -382
- 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/episode-naming.js +182 -182
- 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 +264 -264
- package/services/torrent/torrent-pool.js +2688 -2688
- package/services/torrent/worker/background-fill.js +213 -213
- package/services/torrent/worker/channel.js +271 -271
- package/services/torrent/worker/client.js +836 -836
- 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 +231 -231
- 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 +371 -371
- package/services/torrent/worker/protocol.js +184 -184
- package/services/torrent/worker/resume-warm.js +107 -107
- package/services/torrent/worker/webrtc-shim.js +219 -219
- package/services/torrent/worker/worker.js +1137 -1137
- package/services/transport/data-channel-handler.js +1373 -1373
- 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 +421 -421
- package/services/viewer/PriorityOrchestrator.js +325 -325
- package/services/viewer/Viewer.js +691 -691
- 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/media-time.js +355 -355
- package/services/viewer/playback-readiness.js +635 -635
- 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-buffer-continuity.test.js +77 -77
- package/test/audio-calibration.test.js +23 -23
- package/test/audio-inventory.test.js +212 -212
- package/test/audio-track-bitrate.test.js +40 -40
- package/test/auto-quality-step.test.js +762 -762
- 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/coalesce.test.js +39 -39
- 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 +233 -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 -92
- package/test/dead-torrent-handle.test.js +52 -52
- package/test/declared-rates.test.js +116 -116
- package/test/declared-tracks.test.js +43 -43
- package/test/decode-cost-fit.test.js +202 -202
- package/test/decode-cost.test.js +475 -475
- 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 +275 -275
- package/test/encode-exit.test.js +111 -111
- package/test/encode-orchestrator.test.js +426 -426
- package/test/encode-plan-viewers.test.js +725 -725
- package/test/encode-plan.test.js +545 -545
- package/test/encode-run-state.test.js +314 -314
- package/test/encode-run.test.js +20 -0
- 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 +68 -68
- package/test/episode-naming.test.js +131 -131
- 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/fixtures/audio-frame-boundary.json +1 -1
- 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 +36 -38
- package/test/held-read-ownership.test.js +77 -77
- package/test/held-request-width.test.js +157 -157
- package/test/helpers/encode-run.js +248 -240
- package/test/helpers/held-file.js +77 -77
- package/test/helpers/manager.js +62 -62
- package/test/helpers/matroska-file.js +296 -296
- 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 +123 -123
- 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 +291 -291
- package/test/matroska-cluster-discovery.test.js +323 -323
- package/test/matroska-cues-track.test.js +192 -192
- package/test/media-time.test.js +62 -62
- 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 +273 -273
- 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 +207 -207
- 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 +92 -87
- 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 +401 -401
- 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 +595 -595
- package/test/piece-store-never-refuses.test.js +153 -153
- package/test/piece-store-reservations.test.js +403 -403
- 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/playback-readiness.test.js +518 -518
- package/test/playback-start-regressions.test.js +101 -101
- 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 +160 -160
- package/test/quality-variants.test.js +1274 -1270
- package/test/rate-control-in-the-output.test.js +181 -180
- package/test/read-survives-withdrawal.test.js +164 -164
- package/test/read-window.test.js +365 -365
- package/test/reads-not-yet-arrived.test.js +218 -218
- 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 +64 -64
- package/test/same-height-switch.test.js +663 -663
- 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 +114 -114
- 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 +222 -222
- package/test/segment-serve-wiring.test.js +481 -481
- package/test/segment-store-eviction.test.js +247 -247
- package/test/segment-store.test.js +352 -352
- 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 +111 -111
- 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/soundtrack-rate-bound.test.js +289 -289
- package/test/source-file.test.js +133 -133
- package/test/source-files-route.test.js +113 -113
- 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 +237 -237
- 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 +385 -385
- 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-metadata.test.js +15 -15
- package/test/subtitle-plan-waits.test.js +125 -125
- package/test/subtitle-track-numbering.test.js +391 -391
- package/test/supply-margin.test.js +190 -190
- 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 +508 -508
- 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 +92 -92
- package/test/viewer-starts-on-map.test.js +27 -27
- package/test/viewer-states.test.js +192 -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/whole-file-held-read.test.js +30 -30
- 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/coalesce.js +41 -41
- 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,202 +1,202 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file What is wanted from one torrent right now, and by whom.
|
|
3
|
-
*
|
|
4
|
-
* **One statement, two consumers.** Until 2026-09-02 the same intent was
|
|
5
|
-
* written down twice and reconciled by a third piece of code. A reader told the
|
|
6
|
-
* piece store `protectRange(readerId, from, to)` so its bytes would not be
|
|
7
|
-
* evicted from memory; it separately told the torrent `select(from, to, 1)` so
|
|
8
|
-
* they would be fetched; and the pool then read the store's memory claims every
|
|
9
|
-
* few seconds to rebuild the download claims WebTorrent had dropped once
|
|
10
|
-
* satisfied. One want, three places, and a sweeper between two of them.
|
|
11
|
-
*
|
|
12
|
-
* Here it is stated once. Memory reads this to decide what to keep resident;
|
|
13
|
-
* the swarm layer reads it to decide what to ask peers for. Neither owns it and
|
|
14
|
-
* neither can drift from the other.
|
|
15
|
-
*
|
|
16
|
-
* Knows nothing about WebTorrent, nothing about the piece store, and nothing
|
|
17
|
-
* about pieces — so it is testable with numbers alone.
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import { isConditional, Urgency, URGENCY_ORDER } from "./Urgency.js";
|
|
21
|
-
import { unionOf, Window } from "./Window.js";
|
|
22
|
-
|
|
23
|
-
export class DemandRegister {
|
|
24
|
-
/** Claimant → the one window it currently states. */
|
|
25
|
-
#windows = new Map();
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* State a need, replacing whatever that claimant said before.
|
|
29
|
-
*
|
|
30
|
-
* Replacing rather than adding is the point: a reader walking a film restates
|
|
31
|
-
* a moving window many times a second, and if those accumulated the download
|
|
32
|
-
* set would grow to the whole file within a minute.
|
|
33
|
-
*
|
|
34
|
-
* @param {object} params
|
|
35
|
-
* @param {string} params.claimant
|
|
36
|
-
* @param {number} params.fileIndex
|
|
37
|
-
* @param {number} params.byteStart
|
|
38
|
-
* @param {number} params.byteEnd
|
|
39
|
-
* @param {number} params.urgency
|
|
40
|
-
* @returns {Window}
|
|
41
|
-
*/
|
|
42
|
-
state({ claimant, fileIndex, byteStart, byteEnd, urgency }) {
|
|
43
|
-
const window = new Window({ claimant, fileIndex, byteStart, byteEnd, urgency });
|
|
44
|
-
this.#windows.set(claimant, window);
|
|
45
|
-
return window;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Withdraw what a claimant said.
|
|
50
|
-
*
|
|
51
|
-
* Named by claimant, so a release that arrives twice, or after the claimant
|
|
52
|
-
* has gone, withdraws nothing rather than somebody else's window. Must be
|
|
53
|
-
* called from a `finally`: a reader that ends without withdrawing keeps the
|
|
54
|
-
* swarm fetching for somebody who is no longer there.
|
|
55
|
-
*
|
|
56
|
-
* @param {string} claimant
|
|
57
|
-
* @returns {boolean} Whether there was anything to withdraw.
|
|
58
|
-
*/
|
|
59
|
-
withdraw(claimant) {
|
|
60
|
-
return this.#windows.delete(claimant);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/** How many claimants are stating something. */
|
|
64
|
-
get size() {
|
|
65
|
-
return this.#windows.size;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Every stated window, most urgent first.
|
|
70
|
-
*
|
|
71
|
-
* @returns {Window[]}
|
|
72
|
-
*/
|
|
73
|
-
windows() {
|
|
74
|
-
return [...this.#windows.values()].sort((left, right) => left.urgency - right.urgency);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* The windows at one level of urgency.
|
|
79
|
-
*
|
|
80
|
-
* @param {number} urgency
|
|
81
|
-
* @returns {Window[]}
|
|
82
|
-
*/
|
|
83
|
-
at(urgency) {
|
|
84
|
-
return this.windows().filter((window) => window.urgency === urgency);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* How urgently one byte of one file is wanted, by whoever wants it most.
|
|
89
|
-
*
|
|
90
|
-
* The priority map states its zones here like anybody else, so this answers
|
|
91
|
-
* for a byte nobody is reading yet: it is how a wait is attributed to a level
|
|
92
|
-
* without the reader keeping a forecast of its own.
|
|
93
|
-
*
|
|
94
|
-
* @param {number} fileIndex
|
|
95
|
-
* @param {number} byteOffset
|
|
96
|
-
* @returns {number | null} Null when nothing covers that byte.
|
|
97
|
-
*/
|
|
98
|
-
urgencyAt(fileIndex, byteOffset) {
|
|
99
|
-
// `windows()` is sorted most urgent first, so the first cover is the answer.
|
|
100
|
-
const found = this.windows().find((window) =>
|
|
101
|
-
window.fileIndex === fileIndex &&
|
|
102
|
-
byteOffset >= window.byteStart &&
|
|
103
|
-
byteOffset <= window.byteEnd);
|
|
104
|
-
return found ? found.urgency : null;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* The union of what is wanted of one file at one level, in byte ranges.
|
|
109
|
-
*
|
|
110
|
-
* @param {number} fileIndex
|
|
111
|
-
* @param {number} urgency
|
|
112
|
-
* @returns {Array<{ byteStart: number, byteEnd: number }>}
|
|
113
|
-
*/
|
|
114
|
-
unionFor(fileIndex, urgency) {
|
|
115
|
-
return unionOf(
|
|
116
|
-
this.windows().filter((window) => window.fileIndex === fileIndex && window.urgency === urgency)
|
|
117
|
-
);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* The union of everything wanted of one file, whatever the urgency.
|
|
122
|
-
*
|
|
123
|
-
* This is what memory is sized against: a piece inside any stated window will
|
|
124
|
-
* be read, and evicting it means fetching it back from disk moments later.
|
|
125
|
-
*
|
|
126
|
-
* @param {number} [fileIndex] - Every file when omitted.
|
|
127
|
-
* @returns {Array<{ byteStart: number, byteEnd: number }>}
|
|
128
|
-
*/
|
|
129
|
-
union(fileIndex) {
|
|
130
|
-
const wanted = fileIndex === undefined
|
|
131
|
-
? this.windows()
|
|
132
|
-
: this.windows().filter((window) => window.fileIndex === fileIndex);
|
|
133
|
-
// Ranges of different files must not be merged: byte 100 of file 0 and byte
|
|
134
|
-
// 100 of file 1 are different bytes.
|
|
135
|
-
const byFile = new Map();
|
|
136
|
-
for (const window of wanted) {
|
|
137
|
-
const list = byFile.get(window.fileIndex) ?? [];
|
|
138
|
-
list.push(window);
|
|
139
|
-
byFile.set(window.fileIndex, list);
|
|
140
|
-
}
|
|
141
|
-
return [...byFile.values()].flatMap((list) => unionOf(list));
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/** Which files anybody is stating a need for. */
|
|
145
|
-
files() {
|
|
146
|
-
return [...new Set(this.windows().map((window) => window.fileIndex))].sort((a, b) => a - b);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* The most urgent level anybody is stating, or null when nothing is stated.
|
|
151
|
-
*
|
|
152
|
-
* @returns {number | null}
|
|
153
|
-
*/
|
|
154
|
-
mostUrgent() {
|
|
155
|
-
const windows = this.windows();
|
|
156
|
-
return windows.length === 0 ? null : windows[0].urgency;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Which levels should be stated to the swarm, given what is still missing.
|
|
161
|
-
*
|
|
162
|
-
* The three urgent levels always. The two speculative ones only while
|
|
163
|
-
* everything above them is complete — and withdrawn whole the moment one is
|
|
164
|
-
* not, because a withdrawn window is not in the download set at all and a
|
|
165
|
-
* peer with nothing urgent to give cannot fall through to it. That is the
|
|
166
|
-
* difference from a permanently low priority, which is exactly what lets a
|
|
167
|
-
* peer fall through and spend the shared link on a piece nobody is waiting
|
|
168
|
-
* for.
|
|
169
|
-
*
|
|
170
|
-
* @param {(window: Window) => boolean} isSatisfied - Whether a window has
|
|
171
|
-
* everything it asked for. Given from outside because only the torrent
|
|
172
|
-
* knows what has arrived.
|
|
173
|
-
* @param {boolean} [speculativeAllowed] - Whether anything ELSEWHERE is still
|
|
174
|
-
* waiting for something urgent. False holds the speculative levels back
|
|
175
|
-
* even when this torrent has everything it needs: two films on one proxy
|
|
176
|
-
* share the link, so filling the tail of one while a viewer of the other
|
|
177
|
-
* has a still picture spends the same bandwidth twice over.
|
|
178
|
-
* @returns {number[]} Levels to state, most urgent first.
|
|
179
|
-
*/
|
|
180
|
-
levelsToState(isSatisfied, speculativeAllowed = true) {
|
|
181
|
-
const stating = [];
|
|
182
|
-
let everythingAboveIsSatisfied = speculativeAllowed;
|
|
183
|
-
for (const urgency of URGENCY_ORDER) {
|
|
184
|
-
if (isConditional(urgency) && !everythingAboveIsSatisfied) {
|
|
185
|
-
break;
|
|
186
|
-
}
|
|
187
|
-
stating.push(urgency);
|
|
188
|
-
const level = this.at(urgency);
|
|
189
|
-
if (level.some((window) => !isSatisfied(window))) {
|
|
190
|
-
everythingAboveIsSatisfied = false;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
return stating;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/** Forget everything. The torrent is going. */
|
|
197
|
-
clear() {
|
|
198
|
-
this.#windows.clear();
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
export { Urgency };
|
|
1
|
+
/**
|
|
2
|
+
* @file What is wanted from one torrent right now, and by whom.
|
|
3
|
+
*
|
|
4
|
+
* **One statement, two consumers.** Until 2026-09-02 the same intent was
|
|
5
|
+
* written down twice and reconciled by a third piece of code. A reader told the
|
|
6
|
+
* piece store `protectRange(readerId, from, to)` so its bytes would not be
|
|
7
|
+
* evicted from memory; it separately told the torrent `select(from, to, 1)` so
|
|
8
|
+
* they would be fetched; and the pool then read the store's memory claims every
|
|
9
|
+
* few seconds to rebuild the download claims WebTorrent had dropped once
|
|
10
|
+
* satisfied. One want, three places, and a sweeper between two of them.
|
|
11
|
+
*
|
|
12
|
+
* Here it is stated once. Memory reads this to decide what to keep resident;
|
|
13
|
+
* the swarm layer reads it to decide what to ask peers for. Neither owns it and
|
|
14
|
+
* neither can drift from the other.
|
|
15
|
+
*
|
|
16
|
+
* Knows nothing about WebTorrent, nothing about the piece store, and nothing
|
|
17
|
+
* about pieces — so it is testable with numbers alone.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { isConditional, Urgency, URGENCY_ORDER } from "./Urgency.js";
|
|
21
|
+
import { unionOf, Window } from "./Window.js";
|
|
22
|
+
|
|
23
|
+
export class DemandRegister {
|
|
24
|
+
/** Claimant → the one window it currently states. */
|
|
25
|
+
#windows = new Map();
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* State a need, replacing whatever that claimant said before.
|
|
29
|
+
*
|
|
30
|
+
* Replacing rather than adding is the point: a reader walking a film restates
|
|
31
|
+
* a moving window many times a second, and if those accumulated the download
|
|
32
|
+
* set would grow to the whole file within a minute.
|
|
33
|
+
*
|
|
34
|
+
* @param {object} params
|
|
35
|
+
* @param {string} params.claimant
|
|
36
|
+
* @param {number} params.fileIndex
|
|
37
|
+
* @param {number} params.byteStart
|
|
38
|
+
* @param {number} params.byteEnd
|
|
39
|
+
* @param {number} params.urgency
|
|
40
|
+
* @returns {Window}
|
|
41
|
+
*/
|
|
42
|
+
state({ claimant, fileIndex, byteStart, byteEnd, urgency }) {
|
|
43
|
+
const window = new Window({ claimant, fileIndex, byteStart, byteEnd, urgency });
|
|
44
|
+
this.#windows.set(claimant, window);
|
|
45
|
+
return window;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Withdraw what a claimant said.
|
|
50
|
+
*
|
|
51
|
+
* Named by claimant, so a release that arrives twice, or after the claimant
|
|
52
|
+
* has gone, withdraws nothing rather than somebody else's window. Must be
|
|
53
|
+
* called from a `finally`: a reader that ends without withdrawing keeps the
|
|
54
|
+
* swarm fetching for somebody who is no longer there.
|
|
55
|
+
*
|
|
56
|
+
* @param {string} claimant
|
|
57
|
+
* @returns {boolean} Whether there was anything to withdraw.
|
|
58
|
+
*/
|
|
59
|
+
withdraw(claimant) {
|
|
60
|
+
return this.#windows.delete(claimant);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** How many claimants are stating something. */
|
|
64
|
+
get size() {
|
|
65
|
+
return this.#windows.size;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Every stated window, most urgent first.
|
|
70
|
+
*
|
|
71
|
+
* @returns {Window[]}
|
|
72
|
+
*/
|
|
73
|
+
windows() {
|
|
74
|
+
return [...this.#windows.values()].sort((left, right) => left.urgency - right.urgency);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The windows at one level of urgency.
|
|
79
|
+
*
|
|
80
|
+
* @param {number} urgency
|
|
81
|
+
* @returns {Window[]}
|
|
82
|
+
*/
|
|
83
|
+
at(urgency) {
|
|
84
|
+
return this.windows().filter((window) => window.urgency === urgency);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* How urgently one byte of one file is wanted, by whoever wants it most.
|
|
89
|
+
*
|
|
90
|
+
* The priority map states its zones here like anybody else, so this answers
|
|
91
|
+
* for a byte nobody is reading yet: it is how a wait is attributed to a level
|
|
92
|
+
* without the reader keeping a forecast of its own.
|
|
93
|
+
*
|
|
94
|
+
* @param {number} fileIndex
|
|
95
|
+
* @param {number} byteOffset
|
|
96
|
+
* @returns {number | null} Null when nothing covers that byte.
|
|
97
|
+
*/
|
|
98
|
+
urgencyAt(fileIndex, byteOffset) {
|
|
99
|
+
// `windows()` is sorted most urgent first, so the first cover is the answer.
|
|
100
|
+
const found = this.windows().find((window) =>
|
|
101
|
+
window.fileIndex === fileIndex &&
|
|
102
|
+
byteOffset >= window.byteStart &&
|
|
103
|
+
byteOffset <= window.byteEnd);
|
|
104
|
+
return found ? found.urgency : null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* The union of what is wanted of one file at one level, in byte ranges.
|
|
109
|
+
*
|
|
110
|
+
* @param {number} fileIndex
|
|
111
|
+
* @param {number} urgency
|
|
112
|
+
* @returns {Array<{ byteStart: number, byteEnd: number }>}
|
|
113
|
+
*/
|
|
114
|
+
unionFor(fileIndex, urgency) {
|
|
115
|
+
return unionOf(
|
|
116
|
+
this.windows().filter((window) => window.fileIndex === fileIndex && window.urgency === urgency)
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* The union of everything wanted of one file, whatever the urgency.
|
|
122
|
+
*
|
|
123
|
+
* This is what memory is sized against: a piece inside any stated window will
|
|
124
|
+
* be read, and evicting it means fetching it back from disk moments later.
|
|
125
|
+
*
|
|
126
|
+
* @param {number} [fileIndex] - Every file when omitted.
|
|
127
|
+
* @returns {Array<{ byteStart: number, byteEnd: number }>}
|
|
128
|
+
*/
|
|
129
|
+
union(fileIndex) {
|
|
130
|
+
const wanted = fileIndex === undefined
|
|
131
|
+
? this.windows()
|
|
132
|
+
: this.windows().filter((window) => window.fileIndex === fileIndex);
|
|
133
|
+
// Ranges of different files must not be merged: byte 100 of file 0 and byte
|
|
134
|
+
// 100 of file 1 are different bytes.
|
|
135
|
+
const byFile = new Map();
|
|
136
|
+
for (const window of wanted) {
|
|
137
|
+
const list = byFile.get(window.fileIndex) ?? [];
|
|
138
|
+
list.push(window);
|
|
139
|
+
byFile.set(window.fileIndex, list);
|
|
140
|
+
}
|
|
141
|
+
return [...byFile.values()].flatMap((list) => unionOf(list));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Which files anybody is stating a need for. */
|
|
145
|
+
files() {
|
|
146
|
+
return [...new Set(this.windows().map((window) => window.fileIndex))].sort((a, b) => a - b);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* The most urgent level anybody is stating, or null when nothing is stated.
|
|
151
|
+
*
|
|
152
|
+
* @returns {number | null}
|
|
153
|
+
*/
|
|
154
|
+
mostUrgent() {
|
|
155
|
+
const windows = this.windows();
|
|
156
|
+
return windows.length === 0 ? null : windows[0].urgency;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Which levels should be stated to the swarm, given what is still missing.
|
|
161
|
+
*
|
|
162
|
+
* The three urgent levels always. The two speculative ones only while
|
|
163
|
+
* everything above them is complete — and withdrawn whole the moment one is
|
|
164
|
+
* not, because a withdrawn window is not in the download set at all and a
|
|
165
|
+
* peer with nothing urgent to give cannot fall through to it. That is the
|
|
166
|
+
* difference from a permanently low priority, which is exactly what lets a
|
|
167
|
+
* peer fall through and spend the shared link on a piece nobody is waiting
|
|
168
|
+
* for.
|
|
169
|
+
*
|
|
170
|
+
* @param {(window: Window) => boolean} isSatisfied - Whether a window has
|
|
171
|
+
* everything it asked for. Given from outside because only the torrent
|
|
172
|
+
* knows what has arrived.
|
|
173
|
+
* @param {boolean} [speculativeAllowed] - Whether anything ELSEWHERE is still
|
|
174
|
+
* waiting for something urgent. False holds the speculative levels back
|
|
175
|
+
* even when this torrent has everything it needs: two films on one proxy
|
|
176
|
+
* share the link, so filling the tail of one while a viewer of the other
|
|
177
|
+
* has a still picture spends the same bandwidth twice over.
|
|
178
|
+
* @returns {number[]} Levels to state, most urgent first.
|
|
179
|
+
*/
|
|
180
|
+
levelsToState(isSatisfied, speculativeAllowed = true) {
|
|
181
|
+
const stating = [];
|
|
182
|
+
let everythingAboveIsSatisfied = speculativeAllowed;
|
|
183
|
+
for (const urgency of URGENCY_ORDER) {
|
|
184
|
+
if (isConditional(urgency) && !everythingAboveIsSatisfied) {
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
stating.push(urgency);
|
|
188
|
+
const level = this.at(urgency);
|
|
189
|
+
if (level.some((window) => !isSatisfied(window))) {
|
|
190
|
+
everythingAboveIsSatisfied = false;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return stating;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** Forget everything. The torrent is going. */
|
|
197
|
+
clear() {
|
|
198
|
+
this.#windows.clear();
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export { Urgency };
|