@torrent-tv/proxy 2.88.2 → 2.88.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 +2259 -2246
- 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 +23 -4
- package/docs/container-architecture.md +218 -218
- package/docs/download-architecture.md +261 -261
- package/docs/encode-architecture.md +729 -729
- package/docs/encode-run-state.md +96 -96
- package/docs/logs.md +97 -97
- package/docs/socks5-egress-research.md +114 -114
- package/docs/storage-architecture.md +267 -267
- package/knip.json +14 -14
- package/openspec/changes/adaptive-bitrate/design.md +92 -92
- package/openspec/changes/adaptive-bitrate/proposal.md +56 -56
- package/openspec/changes/adaptive-bitrate/specs/adaptive-bitrate/spec.md +51 -51
- package/openspec/changes/adaptive-bitrate/tasks.md +40 -40
- package/openspec/changes/binary-distribution/.openspec.yaml +2 -2
- package/openspec/changes/binary-distribution/proposal.md +53 -53
- package/openspec/changes/chunked-request-bodies/design.md +81 -81
- package/openspec/changes/chunked-request-bodies/proposal.md +53 -53
- package/openspec/changes/chunked-request-bodies/specs/chunked-request-bodies/spec.md +30 -30
- package/openspec/changes/chunked-request-bodies/tasks.md +40 -40
- package/openspec/changes/cold-start/design.md +98 -98
- package/openspec/changes/cold-start/proposal.md +62 -62
- package/openspec/changes/cold-start/specs/cold-start/spec.md +47 -47
- package/openspec/changes/cold-start/tasks.md +48 -48
- package/openspec/changes/disk-cap/.openspec.yaml +2 -2
- package/openspec/changes/disk-cap/proposal.md +37 -37
- package/openspec/changes/disk-cap/specs/disk-cap/spec.md +25 -25
- package/openspec/changes/disk-cap/tasks.md +19 -19
- package/openspec/changes/proxy-observability/.openspec.yaml +2 -2
- package/openspec/changes/proxy-observability/design.md +38 -38
- package/openspec/changes/proxy-observability/proposal.md +49 -49
- package/openspec/changes/proxy-observability/specs/observability/spec.md +33 -33
- package/openspec/changes/proxy-observability/tasks.md +21 -21
- package/openspec/changes/seek-debounce/design.md +118 -118
- package/openspec/changes/seek-debounce/proposal.md +59 -59
- package/openspec/changes/seek-debounce/specs/seek-debounce/spec.md +41 -41
- package/openspec/changes/seek-debounce/tasks.md +33 -33
- package/openspec/changes/subtitle-language/.openspec.yaml +2 -2
- package/openspec/changes/subtitle-language/proposal.md +50 -50
- package/openspec/changes/subtitle-language/specs/subtitle-language/spec.md +34 -34
- package/openspec/changes/subtitle-language/tasks.md +26 -26
- package/openspec/changes/track-selection/.openspec.yaml +2 -2
- package/openspec/changes/track-selection/design.md +47 -47
- package/openspec/changes/track-selection/proposal.md +45 -45
- package/openspec/changes/track-selection/specs/track-selection/spec.md +41 -41
- package/openspec/changes/track-selection/tasks.md +20 -20
- package/openspec/changes/transcode-quality/.openspec.yaml +2 -2
- package/openspec/changes/transcode-quality/proposal.md +51 -51
- package/openspec/changes/transcode-quality/specs/transcode-quality/spec.md +139 -139
- package/openspec/changes/transcode-quality/tasks.md +63 -63
- package/openspec/config.yaml +38 -38
- package/package.json +1 -1
- package/public/.well-known/appspecific/com.chrome.devtools.json +1 -1
- package/research/delivery-wedge-stand-ladder-2026-09-13.md +329 -329
- package/research/double-spawn-2026-09-10.md +171 -171
- package/research/handover-2026-09-13.md +209 -209
- package/research/handover-reader-claims-removal-2026-09-12.md +166 -166
- package/research/piece-withdrawn-but-still-claimed-2026-09-12.md +175 -175
- package/research/preset-benchmark-size-scaling-2026-08-22.md +55 -55
- package/research/priority-map-is-the-truth-2026-09-12.md +225 -225
- package/routes/api/client-logs/post.js +86 -86
- package/routes/api/delivery-sink/get.js +72 -72
- package/routes/api/link-probe/get.js +51 -51
- package/routes/api/playback-plan/audio-tracks/post.js +29 -29
- package/routes/api/playback-plan/post.js +53 -53
- package/routes/api/sources/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 -173
- 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 +380 -380
- package/routes/transcode/audio-file/get.js +64 -64
- package/routes/transcode/audio-warm/get.js +95 -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 +725 -725
- 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 +1217 -1217
- package/services/encode/EncodePlan.js +1366 -1366
- package/services/encode/EncodeRun.js +813 -813
- package/services/encode/EncodeRuns.js +1235 -1235
- 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 +783 -758
- package/services/encode/OutputTimes.js +579 -579
- package/services/encode/QsvEncoder.js +58 -58
- package/services/encode/Renditions.js +2282 -2184
- 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 +418 -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 +1150 -1141
- 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 +349 -254
- package/services/encode/quality/output-format.js +319 -315
- 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 +754 -754
- package/services/encode/run-costs.js +155 -155
- package/services/encode/run-inputs.js +42 -42
- package/services/encode/segment-formats/fmp4.js +456 -456
- package/services/encode/segment-formats/index.js +85 -85
- package/services/encode/segment-formats/init-compat.js +324 -324
- package/services/encode/segment-formats/mp4-boxes.js +21 -6
- 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 +298 -245
- package/services/media/container/AviContainer.js +266 -266
- package/services/media/container/Container.js +609 -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 +1344 -1244
- 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 +931 -931
- package/services/media/tracks/AudioTrack.js +143 -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/audio-bound.js +233 -0
- 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 +1150 -1150
- package/services/server/ViewerRequests.js +760 -760
- 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/playback-readiness.js +13 -8
- package/services/server/transcode-session-files.js +325 -325
- package/services/server/wire-outputs.js +995 -992
- 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 +1931 -1931
- package/services/storage/returns.js +103 -103
- package/services/storage/segment-store/SegmentStore.js +1240 -1240
- package/services/storage/wire.js +175 -175
- 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 +262 -262
- package/services/torrent/torrent-pool.js +2675 -2675
- package/services/torrent/worker/background-fill.js +213 -213
- package/services/torrent/worker/channel.js +271 -271
- package/services/torrent/worker/client.js +765 -765
- 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 +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 +1129 -1129
- package/services/transport/data-channel-handler.js +1369 -1369
- 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 +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/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 +70 -0
- package/test/audio-inventory.test.js +212 -212
- package/test/audio-track-bitrate.test.js +40 -40
- package/test/auto-quality-step.test.js +707 -707
- package/test/available-share.test.js +76 -76
- package/test/background-fill.test.js +190 -190
- package/test/client-log-file.test.js +110 -110
- package/test/completed-files.test.js +115 -115
- package/test/concurrent-cost.test.js +148 -148
- package/test/container-media-info.test.js +228 -228
- package/test/contention.test.js +76 -76
- package/test/core-dumps.test.js +57 -57
- package/test/coverage-follows-the-disk.test.js +228 -228
- package/test/coverage-map.test.js +195 -195
- package/test/cue-push-carries-what-the-walk-found.test.js +58 -58
- package/test/cut-times-timeline.test.js +65 -65
- package/test/cuts-follow-published-grid.test.js +109 -109
- package/test/data-channel-frame.test.js +63 -63
- package/test/data-channel-viewer-lifecycle.test.js +92 -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 +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 +162 -162
- 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/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 -0
- package/test/fmp4-presentation-clock.test.js +147 -147
- 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 +112 -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 +270 -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 +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 +453 -453
- package/test/playback-start-regressions.test.js +99 -99
- 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 +1257 -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 +105 -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/soundtrack-rate-bound.test.js +289 -0
- 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 +209 -209
- 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 +504 -407
- package/test/upload-hurry.test.js +186 -186
- package/test/usrsctp-state.test.js +131 -131
- package/test/video-facts.test.js +102 -102
- package/test/viewer-assignments.test.js +143 -143
- package/test/viewer-outputs.test.js +285 -285
- package/test/viewer-presence.test.js +116 -116
- package/test/viewer-requests-admission.test.js +89 -89
- package/test/viewer-states.test.js +189 -189
- package/test/viewer-subtitle-subscription.test.js +96 -96
- package/test/viewer.test.js +139 -139
- package/test/viewing-generations.test.js +390 -390
- package/test/visible-rung.test.js +37 -37
- package/test/wait-ledger.test.js +100 -100
- package/test/webrtc-shim.test.js +149 -149
- package/test/wedge-certainty.test.js +131 -131
- package/test/withdraw-piece-claim.test.js +212 -212
- package/test/worker-channel.test.js +158 -158
- package/utils/client-log-file.js +151 -151
- package/utils/logger.js +312 -312
- package/utils/parse-range.js +25 -25
- package/utils/perf.js +59 -59
- package/utils/torrent-source-key.js +38 -38
|
@@ -1,175 +1,175 @@
|
|
|
1
|
-
# A film played 80 seconds and then stood still for 92 minutes — 2026-09-12
|
|
2
|
-
|
|
3
|
-
Proxy 2.83.4, addon 0.74.1, one viewer, `Reacher.S04E07.1080p.rus.LostFilm.TV.mkv`
|
|
4
|
-
(2 363 497 962 bytes). Reported by the user as "the film only managed to play
|
|
5
|
-
one minute nineteen seconds".
|
|
6
|
-
|
|
7
|
-
Every claim below is tagged by where it comes from: **[log]** read out of a
|
|
8
|
-
captured log, **[code]** read out of the source, **[measured]** computed from
|
|
9
|
-
the captured logs, **[derived]** reasoned from two of the above.
|
|
10
|
-
|
|
11
|
-
## 1. What the viewer saw
|
|
12
|
-
|
|
13
|
-
**[log, browser]** Every one of the 1112 `decode` readings in the captured
|
|
14
|
-
window is identical: `t=80.0s size=1920x1080 frames=1920 dropped=0
|
|
15
|
-
readyState=2`. 1920 frames at 24 fps is 80.0 s, so the picture advanced 80
|
|
16
|
-
seconds and then never moved again. The page stayed open until 18:52.
|
|
17
|
-
|
|
18
|
-
**[log, browser]** From then on the player asked for one segment and was
|
|
19
|
-
refused, about once a minute for 92 minutes:
|
|
20
|
-
|
|
21
|
-
```
|
|
22
|
-
18:30:47 non-fatal: fragLoadError currentTime=79.97 msReadyState=open
|
|
23
|
-
frag=segment-00008.mp4 sn=8 action=5 error=Error: HTTP Error 503 …
|
|
24
|
-
18:41:15 fatal: fragLoadError … sn=8 … HTTP Error 503
|
|
25
|
-
18:41:17 recovered fatal networkError, resuming at 80.0s
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
`msReadyState=open` throughout, so this is **not** the closed-MediaSource
|
|
29
|
-
failure of item 86. **[log]** The transport was healthy the whole time —
|
|
30
|
-
`rtt=4ms`, `state=connected ice=connected`, every counter advancing — so it is
|
|
31
|
-
not item 10 either.
|
|
32
|
-
|
|
33
|
-
## 2. What the proxy answered, and why
|
|
34
|
-
|
|
35
|
-
**[code]** 503 in `routes/transcode/session-file/get.js:143` is the
|
|
36
|
-
`warming-up` branch: the segment is still being produced. It was never produced.
|
|
37
|
-
|
|
38
|
-
**[log]** 239 times:
|
|
39
|
-
|
|
40
|
-
```
|
|
41
|
-
transcode 17ec5953 holding segment-00008.mp4: the file is not on disk
|
|
42
|
-
(runs from #8, viewer at #8, encoder alive, index #8, produced 0.0s at 2.23x
|
|
43
|
-
— the encoder has not made it)
|
|
44
|
-
[hold] segment-00008.mp4 warming-up after 60400ms (the map wants it now, rank 69 of 69)
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
**[log]** The encoder could not open its input, 5712 times:
|
|
48
|
-
|
|
49
|
-
```
|
|
50
|
-
encode-run #8..#247 lost its input ([http @ …] Error reading HTTP response: End of file)
|
|
51
|
-
Error opening input file http://127.0.0.1:9090/stream?sourceKey=94dda59f… (8514×)
|
|
52
|
-
Error opening input files: End of file (5712×)
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
**[log]** And `/stream` said exactly what was wrong, 2884 times:
|
|
56
|
-
|
|
57
|
-
```
|
|
58
|
-
stream: read of "Reacher.S04E07.1080p.rus.LostFilm.TV.mkv" bytes 0-2363497961
|
|
59
|
-
ended after 0 of 2363497962 bytes: Piece 0 is verified but absent from the store.
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
Zero bytes of 2.36 GB. ffmpeg reads an empty body as the end of the file.
|
|
63
|
-
|
|
64
|
-
## 3. The cause
|
|
65
|
-
|
|
66
|
-
**[log]** The torrent believed it had the whole file:
|
|
67
|
-
|
|
68
|
-
```
|
|
69
|
-
[stats] 94dda59f 5d276be5 peers=54 connected … file=100.0% header=8388608/8388608B
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
**[log]** The store did not:
|
|
73
|
-
|
|
74
|
-
```
|
|
75
|
-
piece-store "Reacher.S04E07…": resident=27/42 (108MB of 168MB allowed)
|
|
76
|
-
committed=108MB blocks=27 on-disk=26MB pinned=0 spilled=7
|
|
77
|
-
reads=5932 (73.8% from memory) spills=571 revivals=41
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
**[measured]** 571 spills at a 4 MB piece is about 2284 MB written to disk;
|
|
81
|
-
26 MB remained. So roughly 565 pieces had been removed. **[log]** It was not
|
|
82
|
-
the disk cap: `disk: 93634MB free; spilled pieces 26MB of 46656MB` — the
|
|
83
|
-
ceiling never came near binding, and 93 GB were free.
|
|
84
|
-
|
|
85
|
-
**[code]** That leaves exactly one remover: `PieceDiskStore.forgetBehind`,
|
|
86
|
-
called from `SharedPieceStore.reviseSpillCeiling`, which drops every piece
|
|
87
|
-
lying behind the earliest read head. **[log]** The encoder had reached
|
|
88
|
-
`proxyProcessed=725.643`, twelve minutes of film, so piece 0 — the start of the
|
|
89
|
-
file and its Matroska header — was far behind every reader and was dropped.
|
|
90
|
-
That is the rule working as designed.
|
|
91
|
-
|
|
92
|
-
**[code]** `piece-reader.js:151` then asks `torrent.bitfield.get(index)`. The
|
|
93
|
-
library still answered yes, because it had downloaded and hashed that piece
|
|
94
|
-
once. **Nothing ever told it otherwise:** `bitfield.set` does not occur
|
|
95
|
-
anywhere in `services/piece-store/`, `services/torrent-pool.js` or
|
|
96
|
-
`services/torrent-worker/`. So the read concluded the piece was had, asked the
|
|
97
|
-
store, was told it was absent, and threw.
|
|
98
|
-
|
|
99
|
-
**[derived]** And it was never fetched again, because the library does not
|
|
100
|
-
download a piece it believes it owns. The comment on `forgetBehind` states the
|
|
101
|
-
bargain in as many words — "a seek back re-downloads it, which is the same
|
|
102
|
-
bargain this tier makes whenever it drops a piece for room" — and that bargain
|
|
103
|
-
had never once been honoured.
|
|
104
|
-
|
|
105
|
-
**[derived]** The trigger is ordinary: an encoder restart re-opens its input at
|
|
106
|
-
byte 0 (`bytes 0-2363497961` in the log), which is precisely the region
|
|
107
|
-
`forgetBehind` removes first.
|
|
108
|
-
|
|
109
|
-
This is the "un-have problem" the parent `CLAUDE.md` names in the Level 2 disk
|
|
110
|
-
design — "mark the piece incomplete in the completion store → it re-downloads on
|
|
111
|
-
the next read". The eviction shipped; the un-have did not.
|
|
112
|
-
|
|
113
|
-
## 4. Two amplifiers, each a defect of its own
|
|
114
|
-
|
|
115
|
-
### The delay after a lost input did not bind
|
|
116
|
-
|
|
117
|
-
**[code]** `hls-session-manager.js` timed a retry against the DEAD RUN, doubling
|
|
118
|
-
2 s → 15 s. **[code]** `EncodeRun.isAlive` is false in `RETRY_WAIT`, and the
|
|
119
|
-
orchestrator plans from live runs only — so the plan saw the stretch as free and
|
|
120
|
-
placed a new run at once, on every event.
|
|
121
|
-
|
|
122
|
-
**[measured]** 2432 `restart for` lines in 23 minutes: one every 0.57 s, for 61
|
|
123
|
-
minutes, while the delay printed `retrying in 15s`. The comment beside that
|
|
124
|
-
timer predicted this exactly and the code did not prevent it.
|
|
125
|
-
|
|
126
|
-
### The log destroyed the record of its own failure
|
|
127
|
-
|
|
128
|
-
**[measured]** 76 385 lines in 23 minutes and 159 000 in the previous 38 — about
|
|
129
|
-
55 lines a second, of which **52 567 of 76 385 (68.8 %) are byte-identical
|
|
130
|
-
repeats**. **[code]** `MAX_FILE_BYTES` was 32 MiB with one previous turn kept, so
|
|
131
|
-
the file turned over twice inside the session: 61 minutes of 92 survived, and the
|
|
132
|
-
onset — everything before 17:51 — was overwritten by the second rotation and is
|
|
133
|
-
gone. **[log]** `/data` had 91.4 GB free.
|
|
134
|
-
|
|
135
|
-
## 5. What was built (proxy 2.83.5)
|
|
136
|
-
|
|
137
|
-
1. the store announces when it can no longer produce a piece at all — not
|
|
138
|
-
resident, not on disk, not in a file held whole — and the pool withdraws the
|
|
139
|
-
library's claim with `_markUnverified`. The disk tier states only its own
|
|
140
|
-
loss; the store decides whether that is a loss;
|
|
141
|
-
2. a read whose piece is withdrawn under it goes back one step and takes the
|
|
142
|
-
whole ordinary path again instead of throwing. Once per piece;
|
|
143
|
-
3. the delay lives in the orchestrator, per output, and the plan places nothing
|
|
144
|
-
there until it is over — unless something on that output is still producing.
|
|
145
|
-
It is lifted only by a run that actually produced;
|
|
146
|
-
4. an established fact is logged once, then at doubling intervals to a minute,
|
|
147
|
-
saying how many were held back. Matched verbatim; digits are deliberately not
|
|
148
|
-
normalised, because that would merge the memory series;
|
|
149
|
-
5. the log file may reach 1 GB before turning over.
|
|
150
|
-
|
|
151
|
-
## 6. What this does NOT establish
|
|
152
|
-
|
|
153
|
-
1. **Nothing here has been seen in the field.** The next session is the proof.
|
|
154
|
-
What should appear: `withdrawn=` climbing on the piece-store line while reads
|
|
155
|
-
keep succeeding, `its input was not there (attempt N) — placing nothing for
|
|
156
|
-
Xs`, and `[said N more time(s) …]` instead of a flood. What should disappear:
|
|
157
|
-
`Piece N is verified but absent from the store`.
|
|
158
|
-
2. **The onset was not read.** Both log turns begin with the failure already
|
|
159
|
-
established, so which piece failed FIRST, and whether the encoder's first
|
|
160
|
-
input loss had the same cause, is not known. The mechanism is established;
|
|
161
|
-
its first instance is not.
|
|
162
|
-
3. **A consequence, stated rather than discovered later:** a torrent whose
|
|
163
|
-
pieces have been withdrawn is no longer `done`, so a film watched to the end
|
|
164
|
-
is not assembled into a whole file. That is the honest state — those bytes
|
|
165
|
-
are not here — and the previous behaviour was worse than it looked, since the
|
|
166
|
-
assembly reads the file through the store and would have waited for pieces
|
|
167
|
-
the library wrongly believed it had. What is NOT measured is how often a film
|
|
168
|
-
now fails to be assembled, and whether keeping the pieces of a nearly-finished
|
|
169
|
-
file would be worth the disk.
|
|
170
|
-
4. **The re-download cost is not measured.** A seek back past the read heads now
|
|
171
|
-
waits for pieces to be fetched again instead of failing. How long that takes
|
|
172
|
-
on a real swarm, and whether the viewer notices, needs a session.
|
|
173
|
-
5. **`peers=54 connected of -4599 known (…, -1128 queued)`** — the negative
|
|
174
|
-
figures come from WebTorrent's own `_peersLength`/`_numQueued`, read for the
|
|
175
|
-
log only. Not investigated, not touched.
|
|
1
|
+
# A film played 80 seconds and then stood still for 92 minutes — 2026-09-12
|
|
2
|
+
|
|
3
|
+
Proxy 2.83.4, addon 0.74.1, one viewer, `Reacher.S04E07.1080p.rus.LostFilm.TV.mkv`
|
|
4
|
+
(2 363 497 962 bytes). Reported by the user as "the film only managed to play
|
|
5
|
+
one minute nineteen seconds".
|
|
6
|
+
|
|
7
|
+
Every claim below is tagged by where it comes from: **[log]** read out of a
|
|
8
|
+
captured log, **[code]** read out of the source, **[measured]** computed from
|
|
9
|
+
the captured logs, **[derived]** reasoned from two of the above.
|
|
10
|
+
|
|
11
|
+
## 1. What the viewer saw
|
|
12
|
+
|
|
13
|
+
**[log, browser]** Every one of the 1112 `decode` readings in the captured
|
|
14
|
+
window is identical: `t=80.0s size=1920x1080 frames=1920 dropped=0
|
|
15
|
+
readyState=2`. 1920 frames at 24 fps is 80.0 s, so the picture advanced 80
|
|
16
|
+
seconds and then never moved again. The page stayed open until 18:52.
|
|
17
|
+
|
|
18
|
+
**[log, browser]** From then on the player asked for one segment and was
|
|
19
|
+
refused, about once a minute for 92 minutes:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
18:30:47 non-fatal: fragLoadError currentTime=79.97 msReadyState=open
|
|
23
|
+
frag=segment-00008.mp4 sn=8 action=5 error=Error: HTTP Error 503 …
|
|
24
|
+
18:41:15 fatal: fragLoadError … sn=8 … HTTP Error 503
|
|
25
|
+
18:41:17 recovered fatal networkError, resuming at 80.0s
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`msReadyState=open` throughout, so this is **not** the closed-MediaSource
|
|
29
|
+
failure of item 86. **[log]** The transport was healthy the whole time —
|
|
30
|
+
`rtt=4ms`, `state=connected ice=connected`, every counter advancing — so it is
|
|
31
|
+
not item 10 either.
|
|
32
|
+
|
|
33
|
+
## 2. What the proxy answered, and why
|
|
34
|
+
|
|
35
|
+
**[code]** 503 in `routes/transcode/session-file/get.js:143` is the
|
|
36
|
+
`warming-up` branch: the segment is still being produced. It was never produced.
|
|
37
|
+
|
|
38
|
+
**[log]** 239 times:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
transcode 17ec5953 holding segment-00008.mp4: the file is not on disk
|
|
42
|
+
(runs from #8, viewer at #8, encoder alive, index #8, produced 0.0s at 2.23x
|
|
43
|
+
— the encoder has not made it)
|
|
44
|
+
[hold] segment-00008.mp4 warming-up after 60400ms (the map wants it now, rank 69 of 69)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**[log]** The encoder could not open its input, 5712 times:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
encode-run #8..#247 lost its input ([http @ …] Error reading HTTP response: End of file)
|
|
51
|
+
Error opening input file http://127.0.0.1:9090/stream?sourceKey=94dda59f… (8514×)
|
|
52
|
+
Error opening input files: End of file (5712×)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**[log]** And `/stream` said exactly what was wrong, 2884 times:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
stream: read of "Reacher.S04E07.1080p.rus.LostFilm.TV.mkv" bytes 0-2363497961
|
|
59
|
+
ended after 0 of 2363497962 bytes: Piece 0 is verified but absent from the store.
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Zero bytes of 2.36 GB. ffmpeg reads an empty body as the end of the file.
|
|
63
|
+
|
|
64
|
+
## 3. The cause
|
|
65
|
+
|
|
66
|
+
**[log]** The torrent believed it had the whole file:
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
[stats] 94dda59f 5d276be5 peers=54 connected … file=100.0% header=8388608/8388608B
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**[log]** The store did not:
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
piece-store "Reacher.S04E07…": resident=27/42 (108MB of 168MB allowed)
|
|
76
|
+
committed=108MB blocks=27 on-disk=26MB pinned=0 spilled=7
|
|
77
|
+
reads=5932 (73.8% from memory) spills=571 revivals=41
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**[measured]** 571 spills at a 4 MB piece is about 2284 MB written to disk;
|
|
81
|
+
26 MB remained. So roughly 565 pieces had been removed. **[log]** It was not
|
|
82
|
+
the disk cap: `disk: 93634MB free; spilled pieces 26MB of 46656MB` — the
|
|
83
|
+
ceiling never came near binding, and 93 GB were free.
|
|
84
|
+
|
|
85
|
+
**[code]** That leaves exactly one remover: `PieceDiskStore.forgetBehind`,
|
|
86
|
+
called from `SharedPieceStore.reviseSpillCeiling`, which drops every piece
|
|
87
|
+
lying behind the earliest read head. **[log]** The encoder had reached
|
|
88
|
+
`proxyProcessed=725.643`, twelve minutes of film, so piece 0 — the start of the
|
|
89
|
+
file and its Matroska header — was far behind every reader and was dropped.
|
|
90
|
+
That is the rule working as designed.
|
|
91
|
+
|
|
92
|
+
**[code]** `piece-reader.js:151` then asks `torrent.bitfield.get(index)`. The
|
|
93
|
+
library still answered yes, because it had downloaded and hashed that piece
|
|
94
|
+
once. **Nothing ever told it otherwise:** `bitfield.set` does not occur
|
|
95
|
+
anywhere in `services/piece-store/`, `services/torrent-pool.js` or
|
|
96
|
+
`services/torrent-worker/`. So the read concluded the piece was had, asked the
|
|
97
|
+
store, was told it was absent, and threw.
|
|
98
|
+
|
|
99
|
+
**[derived]** And it was never fetched again, because the library does not
|
|
100
|
+
download a piece it believes it owns. The comment on `forgetBehind` states the
|
|
101
|
+
bargain in as many words — "a seek back re-downloads it, which is the same
|
|
102
|
+
bargain this tier makes whenever it drops a piece for room" — and that bargain
|
|
103
|
+
had never once been honoured.
|
|
104
|
+
|
|
105
|
+
**[derived]** The trigger is ordinary: an encoder restart re-opens its input at
|
|
106
|
+
byte 0 (`bytes 0-2363497961` in the log), which is precisely the region
|
|
107
|
+
`forgetBehind` removes first.
|
|
108
|
+
|
|
109
|
+
This is the "un-have problem" the parent `CLAUDE.md` names in the Level 2 disk
|
|
110
|
+
design — "mark the piece incomplete in the completion store → it re-downloads on
|
|
111
|
+
the next read". The eviction shipped; the un-have did not.
|
|
112
|
+
|
|
113
|
+
## 4. Two amplifiers, each a defect of its own
|
|
114
|
+
|
|
115
|
+
### The delay after a lost input did not bind
|
|
116
|
+
|
|
117
|
+
**[code]** `hls-session-manager.js` timed a retry against the DEAD RUN, doubling
|
|
118
|
+
2 s → 15 s. **[code]** `EncodeRun.isAlive` is false in `RETRY_WAIT`, and the
|
|
119
|
+
orchestrator plans from live runs only — so the plan saw the stretch as free and
|
|
120
|
+
placed a new run at once, on every event.
|
|
121
|
+
|
|
122
|
+
**[measured]** 2432 `restart for` lines in 23 minutes: one every 0.57 s, for 61
|
|
123
|
+
minutes, while the delay printed `retrying in 15s`. The comment beside that
|
|
124
|
+
timer predicted this exactly and the code did not prevent it.
|
|
125
|
+
|
|
126
|
+
### The log destroyed the record of its own failure
|
|
127
|
+
|
|
128
|
+
**[measured]** 76 385 lines in 23 minutes and 159 000 in the previous 38 — about
|
|
129
|
+
55 lines a second, of which **52 567 of 76 385 (68.8 %) are byte-identical
|
|
130
|
+
repeats**. **[code]** `MAX_FILE_BYTES` was 32 MiB with one previous turn kept, so
|
|
131
|
+
the file turned over twice inside the session: 61 minutes of 92 survived, and the
|
|
132
|
+
onset — everything before 17:51 — was overwritten by the second rotation and is
|
|
133
|
+
gone. **[log]** `/data` had 91.4 GB free.
|
|
134
|
+
|
|
135
|
+
## 5. What was built (proxy 2.83.5)
|
|
136
|
+
|
|
137
|
+
1. the store announces when it can no longer produce a piece at all — not
|
|
138
|
+
resident, not on disk, not in a file held whole — and the pool withdraws the
|
|
139
|
+
library's claim with `_markUnverified`. The disk tier states only its own
|
|
140
|
+
loss; the store decides whether that is a loss;
|
|
141
|
+
2. a read whose piece is withdrawn under it goes back one step and takes the
|
|
142
|
+
whole ordinary path again instead of throwing. Once per piece;
|
|
143
|
+
3. the delay lives in the orchestrator, per output, and the plan places nothing
|
|
144
|
+
there until it is over — unless something on that output is still producing.
|
|
145
|
+
It is lifted only by a run that actually produced;
|
|
146
|
+
4. an established fact is logged once, then at doubling intervals to a minute,
|
|
147
|
+
saying how many were held back. Matched verbatim; digits are deliberately not
|
|
148
|
+
normalised, because that would merge the memory series;
|
|
149
|
+
5. the log file may reach 1 GB before turning over.
|
|
150
|
+
|
|
151
|
+
## 6. What this does NOT establish
|
|
152
|
+
|
|
153
|
+
1. **Nothing here has been seen in the field.** The next session is the proof.
|
|
154
|
+
What should appear: `withdrawn=` climbing on the piece-store line while reads
|
|
155
|
+
keep succeeding, `its input was not there (attempt N) — placing nothing for
|
|
156
|
+
Xs`, and `[said N more time(s) …]` instead of a flood. What should disappear:
|
|
157
|
+
`Piece N is verified but absent from the store`.
|
|
158
|
+
2. **The onset was not read.** Both log turns begin with the failure already
|
|
159
|
+
established, so which piece failed FIRST, and whether the encoder's first
|
|
160
|
+
input loss had the same cause, is not known. The mechanism is established;
|
|
161
|
+
its first instance is not.
|
|
162
|
+
3. **A consequence, stated rather than discovered later:** a torrent whose
|
|
163
|
+
pieces have been withdrawn is no longer `done`, so a film watched to the end
|
|
164
|
+
is not assembled into a whole file. That is the honest state — those bytes
|
|
165
|
+
are not here — and the previous behaviour was worse than it looked, since the
|
|
166
|
+
assembly reads the file through the store and would have waited for pieces
|
|
167
|
+
the library wrongly believed it had. What is NOT measured is how often a film
|
|
168
|
+
now fails to be assembled, and whether keeping the pieces of a nearly-finished
|
|
169
|
+
file would be worth the disk.
|
|
170
|
+
4. **The re-download cost is not measured.** A seek back past the read heads now
|
|
171
|
+
waits for pieces to be fetched again instead of failing. How long that takes
|
|
172
|
+
on a real swarm, and whether the viewer notices, needs a session.
|
|
173
|
+
5. **`peers=54 connected of -4599 known (…, -1128 queued)`** — the negative
|
|
174
|
+
figures come from WebTorrent's own `_peersLength`/`_numQueued`, read for the
|
|
175
|
+
log only. Not investigated, not touched.
|
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
# Does the preset benchmark's pixels/sec figure hold at a different size? (2026-08-22)
|
|
2
|
-
|
|
3
|
-
Roadmap item 4(e): the software-preset benchmark (`benchmarkSoftwarePresets`,
|
|
4
|
-
`services/hwaccel.js`) measures encoder throughput at one fixed reference size —
|
|
5
|
-
640×360 — and every later use of that figure (deciding which quality rungs this
|
|
6
|
-
host can sustain) scales it by pixel count, assuming cost per pixel is constant
|
|
7
|
-
regardless of frame size. This was never checked against a second size.
|
|
8
|
-
|
|
9
|
-
## Measurement
|
|
10
|
-
|
|
11
|
-
Same clip (`cal-h264-1080-hi.mp4`), same `libx264` presets, same measurement
|
|
12
|
-
method as the shipped benchmark (slope between two `-progress` reports,
|
|
13
|
-
excluding startup) — run once at the existing 640×360 reference and once at
|
|
14
|
-
1280×720 (4× the pixel count), on the developer's desktop:
|
|
15
|
-
|
|
16
|
-
| preset | 640×360 | 1280×720 | ratio (should be ~1.0) |
|
|
17
|
-
|-----------|----------------|----------------|-------------------------|
|
|
18
|
-
| ultrafast | 1322.8 Mpx/s | 588.6 Mpx/s | 0.445 |
|
|
19
|
-
| fast | 176.6 Mpx/s | 128.1 Mpx/s | 0.725 |
|
|
20
|
-
|
|
21
|
-
Script: ad-hoc, not committed (reused the benchmark's own ffmpeg arguments at a
|
|
22
|
-
second `-vf scale=`/`-s` size; available on request if this needs re-running).
|
|
23
|
-
|
|
24
|
-
## Reading
|
|
25
|
-
|
|
26
|
-
The assumption does not hold. Cost per pixel RISES with frame size on this
|
|
27
|
-
host — by 2.25× for `ultrafast`, 1.38× for `fast`, at 4× the pixel count — and
|
|
28
|
-
the effect is preset-dependent, not a single constant. A quality ladder priced
|
|
29
|
-
from the 640×360 figure alone over-states what this host can sustain at 1080p,
|
|
30
|
-
worse for the faster presets that the ladder falls back to under load.
|
|
31
|
-
|
|
32
|
-
## Why this does not become a code change yet
|
|
33
|
-
|
|
34
|
-
One host, one clip, two sizes. [[feedback_pool_diversity]] — a correction fitted
|
|
35
|
-
to this desktop and applied to every proxy in the pool would be exactly the
|
|
36
|
-
mistake that memory exists to prevent: the pool spans ARM boards (HA Yellow /
|
|
37
|
-
CM4) with different cache sizes and memory bandwidth, where a size penalty of
|
|
38
|
-
this shape could be smaller, larger, or absent. [[feedback_no_fudged_numbers]] —
|
|
39
|
-
publishing 0.445/0.725 as universal constants is fabricating a number for hosts
|
|
40
|
-
that were never measured.
|
|
41
|
-
|
|
42
|
-
The benchmark already measures per-host (item 4(f), 4(h)); a size-scaling
|
|
43
|
-
correction belongs there too — the benchmark itself gains a second, cheap
|
|
44
|
-
measurement point (one preset, one second size) and derives its own exponent
|
|
45
|
-
from that host's two readings, the same way `fitDecodeCost` derives its terms
|
|
46
|
-
from clips rather than from a stated formula. That is the remaining work under
|
|
47
|
-
item 4(e); this note is the measurement that justifies doing it, not the fix
|
|
48
|
-
itself.
|
|
49
|
-
|
|
50
|
-
## What is verified
|
|
51
|
-
|
|
52
|
-
The measurement method matches the shipped benchmark exactly (real footage,
|
|
53
|
-
raw frames decoded once, slope between two progress reports) — the only
|
|
54
|
-
variable changed was the target size, so this is not comparing two different
|
|
55
|
-
methodologies.
|
|
1
|
+
# Does the preset benchmark's pixels/sec figure hold at a different size? (2026-08-22)
|
|
2
|
+
|
|
3
|
+
Roadmap item 4(e): the software-preset benchmark (`benchmarkSoftwarePresets`,
|
|
4
|
+
`services/hwaccel.js`) measures encoder throughput at one fixed reference size —
|
|
5
|
+
640×360 — and every later use of that figure (deciding which quality rungs this
|
|
6
|
+
host can sustain) scales it by pixel count, assuming cost per pixel is constant
|
|
7
|
+
regardless of frame size. This was never checked against a second size.
|
|
8
|
+
|
|
9
|
+
## Measurement
|
|
10
|
+
|
|
11
|
+
Same clip (`cal-h264-1080-hi.mp4`), same `libx264` presets, same measurement
|
|
12
|
+
method as the shipped benchmark (slope between two `-progress` reports,
|
|
13
|
+
excluding startup) — run once at the existing 640×360 reference and once at
|
|
14
|
+
1280×720 (4× the pixel count), on the developer's desktop:
|
|
15
|
+
|
|
16
|
+
| preset | 640×360 | 1280×720 | ratio (should be ~1.0) |
|
|
17
|
+
|-----------|----------------|----------------|-------------------------|
|
|
18
|
+
| ultrafast | 1322.8 Mpx/s | 588.6 Mpx/s | 0.445 |
|
|
19
|
+
| fast | 176.6 Mpx/s | 128.1 Mpx/s | 0.725 |
|
|
20
|
+
|
|
21
|
+
Script: ad-hoc, not committed (reused the benchmark's own ffmpeg arguments at a
|
|
22
|
+
second `-vf scale=`/`-s` size; available on request if this needs re-running).
|
|
23
|
+
|
|
24
|
+
## Reading
|
|
25
|
+
|
|
26
|
+
The assumption does not hold. Cost per pixel RISES with frame size on this
|
|
27
|
+
host — by 2.25× for `ultrafast`, 1.38× for `fast`, at 4× the pixel count — and
|
|
28
|
+
the effect is preset-dependent, not a single constant. A quality ladder priced
|
|
29
|
+
from the 640×360 figure alone over-states what this host can sustain at 1080p,
|
|
30
|
+
worse for the faster presets that the ladder falls back to under load.
|
|
31
|
+
|
|
32
|
+
## Why this does not become a code change yet
|
|
33
|
+
|
|
34
|
+
One host, one clip, two sizes. [[feedback_pool_diversity]] — a correction fitted
|
|
35
|
+
to this desktop and applied to every proxy in the pool would be exactly the
|
|
36
|
+
mistake that memory exists to prevent: the pool spans ARM boards (HA Yellow /
|
|
37
|
+
CM4) with different cache sizes and memory bandwidth, where a size penalty of
|
|
38
|
+
this shape could be smaller, larger, or absent. [[feedback_no_fudged_numbers]] —
|
|
39
|
+
publishing 0.445/0.725 as universal constants is fabricating a number for hosts
|
|
40
|
+
that were never measured.
|
|
41
|
+
|
|
42
|
+
The benchmark already measures per-host (item 4(f), 4(h)); a size-scaling
|
|
43
|
+
correction belongs there too — the benchmark itself gains a second, cheap
|
|
44
|
+
measurement point (one preset, one second size) and derives its own exponent
|
|
45
|
+
from that host's two readings, the same way `fitDecodeCost` derives its terms
|
|
46
|
+
from clips rather than from a stated formula. That is the remaining work under
|
|
47
|
+
item 4(e); this note is the measurement that justifies doing it, not the fix
|
|
48
|
+
itself.
|
|
49
|
+
|
|
50
|
+
## What is verified
|
|
51
|
+
|
|
52
|
+
The measurement method matches the shipped benchmark exactly (real footage,
|
|
53
|
+
raw frames decoded once, slope between two progress reports) — the only
|
|
54
|
+
variable changed was the target size, so this is not comparing two different
|
|
55
|
+
methodologies.
|