@torrent-tv/proxy 2.86.0 → 2.87.0
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/CHANGELOG.md +2138 -1915
- package/CLAUDE.md +318 -197
- package/assets/calibration/NOTICE.md +2 -2
- package/bin/cli.js +684 -616
- package/biome.json +409 -233
- package/docs/container-architecture.md +218 -213
- package/docs/download-architecture.md +7 -7
- package/docs/encode-architecture.md +287 -11
- package/docs/encode-run-state.md +2 -2
- package/docs/logs.md +97 -97
- package/docs/{disk-architecture.md → storage-architecture.md} +107 -1
- package/openspec/changes/adaptive-bitrate/design.md +2 -2
- package/openspec/changes/adaptive-bitrate/proposal.md +1 -1
- package/openspec/changes/adaptive-bitrate/tasks.md +1 -1
- package/openspec/changes/chunked-request-bodies/design.md +1 -1
- package/openspec/changes/chunked-request-bodies/proposal.md +1 -1
- package/openspec/changes/chunked-request-bodies/tasks.md +1 -1
- package/openspec/changes/cold-start/design.md +1 -1
- package/openspec/changes/disk-cap/proposal.md +1 -1
- package/openspec/changes/proxy-observability/proposal.md +2 -2
- package/openspec/changes/track-selection/proposal.md +1 -1
- package/openspec/changes/transcode-quality/proposal.md +1 -1
- package/package.json +2 -2
- package/research/delivery-wedge-stand-ladder-2026-09-13.md +329 -329
- package/research/double-spawn-2026-09-10.md +171 -171
- package/routes/api/playback-plan/post.js +2 -2
- package/routes/api/sources/files/get.js +1 -1
- package/routes/api/sources/stats/get.js +1 -1
- package/routes/api/sources/warm/post.js +10 -3
- package/routes/api/subtitles/get.js +230 -230
- package/routes/api/transcode-sessions/fragment-far/post.js +3 -3
- package/routes/api/transcode-sessions/net-report/post.js +129 -94
- package/routes/api/transcode-sessions/post.js +160 -139
- package/routes/api/transcode-sessions/progress/get.js +3 -3
- package/routes/api/transcode-sessions/release/post.js +3 -3
- package/routes/api/transcode-sessions/seek/post.js +12 -3
- package/routes/stream/get.js +2 -2
- package/routes/transcode/audio-file/get.js +15 -6
- package/routes/transcode/audio-warm/get.js +8 -5
- package/routes/transcode/session-file/get.js +429 -268
- package/routes/transcode/variant-file/get.js +47 -7
- package/routes/transcode/variant-warm/get.js +18 -6
- package/scripts/render-run-graph.js +1 -1
- package/server.js +720 -465
- package/services/encode/CoverageMap.js +428 -428
- package/services/encode/CushionReport.js +345 -0
- package/services/encode/EncodeAdmission.js +332 -0
- package/services/{orchestrators → encode}/EncodeOrchestrator.js +285 -17
- package/services/encode/EncodeRun.js +16 -9
- package/services/encode/EncodeRuns.js +1219 -0
- package/services/encode/Encoder.js +18 -2
- package/services/encode/EncoderSelection.js +38 -0
- package/services/encode/LocalObservations.js +359 -0
- package/services/encode/NvencEncoder.js +5 -0
- package/services/encode/OutputOpening.js +745 -0
- package/services/encode/OutputTimes.js +579 -0
- package/services/encode/Renditions.js +2162 -0
- package/services/encode/RunProgress.js +84 -0
- package/services/encode/SoftwareEncoder.js +12 -7
- package/services/encode/args.js +402 -186
- package/services/encode/calibration.js +361 -0
- package/services/encode/contention.js +128 -128
- package/services/encode/encode-run-state.js +16 -20
- package/services/encode/fingerprint.js +322 -0
- package/services/{hwaccel.js → encode/hwaccel.js} +82 -42
- package/services/{learned-median.js → encode/learned-median.js} +11 -0
- package/services/encode/output/EncodedOutput.js +57 -0
- package/services/encode/output/Output.js +131 -0
- package/services/{output/LiveOutputs.js → encode/output/OutputCatalog.js} +141 -40
- package/services/encode/output/OutputSpec.js +390 -0
- package/services/{output → encode/output}/Timeline.js +12 -37
- package/services/encode/output/index.js +20 -0
- package/services/encode/output/ladder.js +32 -0
- package/services/{output → encode/output}/playlists.js +3 -0
- package/services/encode/output-key-format.js +21 -0
- package/services/encode/quality/EncodeCost.js +1083 -0
- package/services/encode/quality/HostLoad.js +525 -0
- package/services/encode/quality/HostTimings.js +249 -0
- package/services/encode/quality/OutputQualityState.js +28 -0
- package/services/encode/quality/QualityController.js +931 -0
- package/services/encode/quality/QualityOffer.js +416 -0
- package/services/encode/quality/drain-threat.js +47 -0
- package/services/encode/quality/link-budget.js +254 -0
- package/services/encode/quality/output-format.js +266 -0
- package/services/encode/quality/serving-output.js +216 -0
- package/services/encode/quality/visible-rung.js +57 -0
- package/services/encode/run-command.js +23 -20
- package/services/encode/run-inputs.js +42 -0
- package/services/{segment-formats → encode/segment-formats}/fmp4.js +63 -0
- package/services/{segment-formats → encode/segment-formats}/index.js +3 -3
- package/services/encode/segment-formats/init-compat.js +324 -0
- package/services/{segment-formats → encode/segment-formats}/mp4-boxes.js +8 -3
- package/services/encode/throughput.js +97 -0
- package/services/{orchestrators → media}/ContainerOrchestrator.js +45 -7
- package/services/media/KeyframeTables.js +316 -0
- package/services/{source → media}/SourceFile.js +7 -55
- package/services/{torrent-worker/subtitle-cues.js → media/SubtitleCues.js} +103 -132
- package/services/media/SubtitleOrchestrator.js +167 -0
- package/services/{audio-inventory.js → media/audio-inventory.js} +7 -1
- package/services/{container → media/container}/AviContainer.js +266 -266
- package/services/{container → media/container}/Container.js +596 -601
- package/services/media/container/ContainerFactory.js +56 -0
- package/services/media/container/KeyframeTable.js +272 -0
- package/services/{container → media/container}/MatroskaContainer.js +1485 -1485
- package/services/{container → media/container}/Mp4Container.js +1244 -1244
- package/services/{container → media/container}/SubtitleFileContainer.js +322 -322
- package/services/media/keyframe-probe.js +157 -0
- package/services/media/media-info-probe.js +93 -0
- package/services/{playback-planner.js → media/playback-planner.js} +82 -39
- package/services/{tracks → media/tracks}/AudioTrack.js +131 -131
- package/services/{tracks → media/tracks}/language-detect.js +174 -174
- package/services/server/OutputLifecycle.js +418 -0
- package/services/server/SegmentServing.js +1146 -0
- package/services/server/ViewerRequests.js +522 -0
- package/services/server/controllers/PlaybackController.js +36 -0
- package/services/{controllers → server/controllers}/SubtitleController.js +17 -33
- package/services/server/wire-outputs.js +958 -0
- package/services/storage/Diagnostics.js +202 -0
- package/services/storage/MachineBudget.js +235 -0
- package/services/{piece-store → storage}/allowance.js +107 -107
- package/services/{core-dumps.js → storage/core-dumps.js} +1 -1
- package/services/{files → storage/files}/CompletedFiles.js +116 -1
- package/services/{disk → storage}/free.js +33 -0
- package/services/storage/machine-memory.js +56 -0
- package/services/{memory-report.js → storage/memory-report.js} +575 -596
- package/services/{piece-store → storage/piece-store}/piece-disk-store.js +0 -7
- package/services/{piece-store → storage/piece-store}/shared-piece-store.js +28 -21
- package/services/{encode → storage/segment-store}/SegmentStore.js +395 -8
- package/services/storage/wire.js +175 -0
- package/services/{download → torrent/download}/SwarmSelection.js +2 -2
- package/services/{download → torrent/download}/registry.js +2 -2
- package/services/torrent/files.js +1 -1
- package/services/torrent/piece-store-of.js +29 -0
- package/services/{torrent-pool.js → torrent/torrent-pool.js} +26 -119
- package/services/{torrent-worker → torrent/worker}/background-fill.js +2 -2
- package/services/{torrent-worker → torrent/worker}/channel.js +271 -250
- package/services/{torrent-worker → torrent/worker}/client.js +77 -58
- package/services/torrent/worker/held-bytes.js +155 -0
- package/services/{torrent-worker → torrent/worker}/install-webrtc-shim.js +1 -1
- package/services/{torrent-worker → torrent/worker}/piece-reader.js +21 -30
- package/services/{torrent-worker → torrent/worker}/pool-adapter.js +101 -137
- package/services/{torrent-worker → torrent/worker}/protocol.js +26 -30
- package/services/torrent/worker/resume-warm.js +107 -0
- package/services/{torrent-worker → torrent/worker}/webrtc-shim.js +2 -2
- package/services/{torrent-worker → torrent/worker}/worker.js +68 -159
- package/services/{data-channel-handler.js → transport/data-channel-handler.js} +1356 -1356
- package/services/{delivery-probe.js → transport/delivery-probe.js} +842 -842
- package/services/{health-collector.js → transport/health-collector.js} +6 -35
- package/services/{packet-witness.js → transport/packet-witness.js} +808 -784
- package/services/{port-mapper.js → transport/port-mapper.js} +1 -1
- package/services/{usrsctp-state.js → transport/usrsctp-state.js} +1 -1
- package/services/{webrtc-manager.js → transport/webrtc-manager.js} +1 -1
- package/services/viewer/Assignments.js +340 -0
- package/services/{priority → viewer}/PriorityMap.js +390 -390
- package/services/{priority → viewer}/PriorityOrchestrator.js +325 -320
- package/services/viewer/Viewer.js +657 -483
- package/services/viewer/Viewers.js +368 -257
- package/services/viewer/active-output.js +37 -54
- package/services/viewer/audio-start.js +4 -3
- package/services/viewer/buffer-trend.js +81 -0
- package/services/viewer/choices.js +647 -0
- package/services/viewer/positions.js +126 -120
- package/services/viewer/report-intake.js +46 -45
- package/test/asset-paths.test.js +28 -0
- package/test/audio-inventory.test.js +3 -3
- package/test/audio-track-bitrate.test.js +40 -0
- package/test/auto-quality-step.test.js +707 -515
- package/test/available-share.test.js +1 -1
- package/test/background-fill.test.js +1 -1
- package/test/client-log-file.test.js +110 -110
- package/test/completed-files.test.js +1 -1
- package/test/concurrent-cost.test.js +13 -3
- package/test/container-media-info.test.js +3 -3
- package/test/core-dumps.test.js +1 -1
- package/test/coverage-follows-the-disk.test.js +40 -8
- package/test/cue-push-carries-what-the-walk-found.test.js +58 -0
- package/test/cut-times-timeline.test.js +2 -2
- package/test/cuts-follow-published-grid.test.js +9 -8
- package/test/data-channel-frame.test.js +1 -1
- package/test/dead-torrent-handle.test.js +1 -1
- package/test/declared-rates.test.js +2 -2
- package/test/declared-tracks.test.js +2 -2
- package/test/decode-cost-fit.test.js +1 -1
- package/test/decode-cost.test.js +34 -25
- package/test/delivery-probe.test.js +1 -1
- package/test/demand-register.test.js +4 -4
- package/test/dht-bootstrap.test.js +1 -1
- package/test/drain-threat.test.js +65 -0
- package/test/duplicate-add.test.js +1 -1
- package/test/encode-admission.test.js +146 -0
- package/test/encode-cost.test.js +199 -10
- package/test/encode-orchestrator.test.js +39 -1
- package/test/encode-plan-viewers.test.js +2 -2
- package/test/encode-run.test.js +2 -2
- package/test/encode-slope.test.js +1 -1
- package/test/encoded-output.test.js +39 -0
- package/test/encoder-readings.test.js +1 -1
- package/test/every-claimant-is-told-its-share.test.js +152 -0
- package/test/failed-encoder-still-serves-what-exists.test.js +354 -0
- package/test/failed-start-breaker.test.js +81 -30
- package/test/fastest-wires.test.js +1 -1
- package/test/ffmpeg-stream-counts.test.js +1 -1
- package/test/file-edges.test.js +3 -3
- package/test/fmp4-self-contained.test.js +1 -1
- package/test/fmp4-timestamps.test.js +1 -1
- package/test/fmp4-track-completeness.test.js +1 -1
- package/test/format-keyed-outputs.test.js +78 -0
- package/test/health-metrics.test.js +3 -2
- package/test/held-request-width.test.js +22 -23
- package/test/helpers/encode-run.js +136 -136
- package/test/helpers/held-file.js +29 -0
- package/test/helpers/manager.js +57 -0
- package/test/helpers/output-spec.js +63 -0
- package/test/host-load.test.js +1 -1
- package/test/index-accuracy-is-about-the-file.test.js +128 -0
- package/test/init-compat.test.js +219 -0
- package/test/input-lost-quiets-the-plan.test.js +1 -1
- package/test/input-unavailable.test.js +1 -1
- package/test/keeping-period.test.js +4 -4
- package/test/keyframe-index-accuracy.test.js +97 -45
- package/test/keyframe-table-budget.test.js +28 -33
- package/test/keyframes-belong-to-the-file.test.js +174 -57
- package/test/learned-median.test.js +1 -1
- package/test/letting-go-asks-who-is-watching.test.js +191 -0
- package/test/limit-row-by-area.test.js +60 -0
- package/test/link-budget.test.js +111 -0
- package/test/link-probe.test.js +56 -56
- package/test/local-encoding-data.test.js +273 -0
- package/test/look-ahead-coverage.test.js +1 -1
- package/test/manager-surface.test.js +294 -0
- package/test/matroska-blocks.test.js +0 -0
- package/test/matroska-cues-track.test.js +1 -1
- package/test/memory-budget.test.js +2 -2
- package/test/mp4-composition-times.test.js +0 -0
- package/test/mp4-subtitles.test.js +1 -1
- package/test/one-authority.test.js +45 -41
- package/test/one-budget.test.js +149 -0
- package/test/one-output-one-session.test.js +204 -0
- package/test/{disk-space.test.js → one-resource-divided.test.js} +30 -10
- package/test/one-serving-output-per-height.test.js +458 -0
- package/test/orchestrator-wired.test.js +46 -45
- package/test/output-catalog.test.js +39 -0
- package/test/output-shape.test.js +4 -51
- package/test/output-spec.test.js +251 -157
- package/test/output-speed.test.js +17 -15
- package/test/outputs-being-prepared.test.js +40 -0
- package/test/packet-witness-ring.test.js +2 -2
- package/test/packet-witness.test.js +1 -1
- package/test/piece-disk-store.test.js +2 -2
- package/test/piece-from-whole-file.test.js +1 -1
- package/test/piece-lru.test.js +1 -1
- package/test/piece-reader.test.js +2 -2
- package/test/piece-store-eviction.test.js +1 -1
- package/test/piece-store-never-refuses.test.js +1 -1
- package/test/piece-store-reservations.test.js +1 -1
- package/test/piece-store-slow-disk.test.js +1 -1
- package/test/piece-tail.test.js +1 -1
- package/test/plan-host-timings.test.js +1 -1
- package/test/plan-is-steady.test.js +225 -225
- package/test/priority-map-download.test.js +4 -4
- package/test/priority-map-emptied.test.js +204 -205
- package/test/priority-map-per-output.test.js +215 -209
- package/test/priority-map.test.js +1 -1
- package/test/probe-wedge-certainty.test.js +1 -1
- package/test/produced-copy-choice.test.js +15 -60
- package/test/quality-offer.test.js +162 -0
- package/test/quality-variants.test.js +1229 -1167
- package/test/rate-control-in-the-output.test.js +180 -0
- package/test/read-survives-withdrawal.test.js +2 -2
- package/test/read-window.test.js +10 -3
- package/test/resume-warm.test.js +1 -1
- package/test/run-inputs.test.js +80 -0
- package/test/run-position-follows-published-grid.test.js +13 -11
- package/test/run-progress.test.js +65 -0
- package/test/same-height-switch.test.js +664 -0
- package/test/seek-frees-the-viewers-requests.test.js +217 -0
- package/test/seek-landing.test.js +13 -13
- package/test/seek-target-not-superseded.test.js +28 -29
- package/test/segment-closed-on-both-branches.test.js +96 -0
- package/test/segment-cut-times.test.js +1 -1
- package/test/segment-hold-supersede.test.js +28 -35
- package/test/segment-judged-by-its-own-header.test.js +221 -0
- package/test/segment-serve-wiring.test.js +130 -39
- package/test/segment-store-eviction.test.js +22 -7
- package/test/segment-store.test.js +98 -14
- package/test/segment-true-start.test.js +1 -1
- package/test/segments-are-shared.test.js +17 -16
- package/test/send-chunk-bytes.test.js +1 -1
- package/test/serving-existing-output.test.js +102 -0
- package/test/serving-output.test.js +102 -0
- package/test/shared-piece-store.test.js +1 -1
- package/test/source-file.test.js +1 -1
- package/test/stale-request-after-seek.test.js +17 -21
- package/test/startup-readings.test.js +10 -5
- package/test/subtitle-cue-framing.test.js +5 -5
- package/test/subtitle-cue-source.test.js +36 -38
- package/test/subtitle-cue-walk.test.js +11 -10
- package/test/subtitle-defaults.test.js +1 -1
- package/test/subtitle-language.test.js +4 -4
- package/test/subtitle-track-numbering.test.js +5 -4
- package/test/supply-margin.test.js +1 -1
- package/test/swarm-follows-readers.test.js +3 -3
- package/test/swarm-reach.test.js +1 -1
- package/test/swarm-selection.test.js +3 -3
- package/test/tail-duplication.test.js +1 -1
- package/test/the-channel-has-both-ends.test.js +91 -0
- package/test/the-torrent-thread-serves-bytes.test.js +107 -0
- package/test/thread-pool.test.js +3 -3
- package/test/timeline.test.js +1 -12
- package/test/torrent-cost.test.js +1 -1
- package/test/tracks-begin-together.test.js +38 -24
- package/test/tunnel-renewal.test.js +1 -1
- package/test/two-viewers-one-picture.test.js +120 -87
- package/test/upload-hurry.test.js +3 -3
- package/test/usrsctp-state.test.js +1 -1
- package/test/video-facts.test.js +1 -1
- package/test/viewer-assignments.test.js +143 -0
- package/test/viewer-outputs.test.js +60 -52
- package/test/viewer-presence.test.js +22 -25
- package/test/viewer-requests-admission.test.js +89 -0
- package/test/viewer-states.test.js +189 -202
- package/test/viewer-subtitle-subscription.test.js +1 -1
- package/test/viewer.test.js +139 -136
- package/test/viewing-generations.test.js +390 -0
- package/test/visible-rung.test.js +37 -0
- package/test/wait-ledger.test.js +1 -1
- package/test/webrtc-shim.test.js +3 -3
- package/test/wedge-certainty.test.js +2 -2
- package/test/withdraw-piece-claim.test.js +2 -2
- package/test/worker-channel.test.js +1 -31
- package/utils/client-log-file.js +151 -151
- package/scripts/read-container-index.mjs +0 -108
- package/services/container/ContainerFactory.js +0 -122
- package/services/controllers/PlaybackController.js +0 -33
- package/services/disk/DiskSpace.js +0 -150
- package/services/disk/wire.js +0 -61
- package/services/hls-session-manager.js +0 -9608
- package/services/orchestrators/SubtitleOrchestrator.js +0 -160
- package/services/output/Output.js +0 -94
- package/services/output/OutputSpec.js +0 -202
- package/services/output/index.js +0 -1
- package/services/output/ladder.js +0 -26
- package/services/produced-index.js +0 -222
- package/services/quality/EncodeCost.js +0 -555
- package/services/torrent-worker/container-tracks.js +0 -414
- package/services/viewer/link-readings.js +0 -56
- package/test/produced-index.test.js +0 -142
- /package/{services → bin}/thread-pool.js +0 -0
- /package/services/{available-share.js → encode/available-share.js} +0 -0
- /package/services/{decode-cost-fit.js → encode/decode-cost-fit.js} +0 -0
- /package/services/{encoder-readings.js → encode/encoder-readings.js} +0 -0
- /package/services/{host-load.js → encode/host-load.js} +0 -0
- /package/services/{output → encode/output}/cut-grid.js +0 -0
- /package/services/{output → encode/output}/rates.js +0 -0
- /package/services/{segment-formats → encode/segment-formats}/mpegts.js +0 -0
- /package/services/{torrent-cost.js → encode/torrent-cost.js} +0 -0
- /package/services/{container → media/container}/ebml-reader.js +0 -0
- /package/services/{ffmpeg-banner.js → media/ffmpeg-banner.js} +0 -0
- /package/services/{tracks → media/tracks}/ContainerTrack.js +0 -0
- /package/services/{tracks → media/tracks}/ImageSubtitleTrack.js +0 -0
- /package/services/{tracks → media/tracks}/SubtitleTrack.js +0 -0
- /package/services/{tracks → media/tracks}/TextSubtitleTrack.js +0 -0
- /package/services/{tracks → media/tracks}/VideoTrack.js +0 -0
- /package/services/{tracks → media/tracks}/index.js +0 -0
- /package/services/{files → storage/files}/piece-from-whole-file.js +0 -0
- /package/services/{disk → storage}/keep.js +0 -0
- /package/services/{piece-store → storage/piece-store}/piece-lru.js +0 -0
- /package/services/{disk → storage}/returns.js +0 -0
- /package/services/{demand → torrent/demand}/DemandRegister.js +0 -0
- /package/services/{demand → torrent/demand}/Urgency.js +0 -0
- /package/services/{demand → torrent/demand}/Window.js +0 -0
- /package/services/{demand → torrent/demand}/index.js +0 -0
- /package/services/{demand → torrent/demand}/pieces.js +0 -0
- /package/services/{download → torrent/download}/withdraw-claim.js +0 -0
- /package/services/{supply-margin.js → torrent/supply-margin.js} +0 -0
- /package/services/{torrent-source-key.js → torrent/torrent-source-key.js} +0 -0
- /package/services/{torrent-worker → torrent/worker}/fastest-wires.js +0 -0
- /package/services/{torrent-worker → torrent/worker}/handle-state.js +0 -0
- /package/services/{nat-classifier.js → transport/nat-classifier.js} +0 -0
- /package/services/{registry-api.js → transport/registry-api.js} +0 -0
- /package/services/{tunnel-client.js → transport/tunnel-client.js} +0 -0
- /package/services/{priority → viewer}/WaitLedger.js +0 -0
|
@@ -7,7 +7,7 @@ ceilings, each standing for the whole disk.
|
|
|
7
7
|
| what | where | what bounded it before |
|
|
8
8
|
|---|---|---|
|
|
9
9
|
| segments an encoder produced | `services/encode/SegmentStore.js` | a quarter of what was free, plus a 2 GB floor — both numbers chosen by hand |
|
|
10
|
-
| pieces the memory store spilled | `services/piece-store/piece-disk-store.js` | **nothing at all** |
|
|
10
|
+
| pieces the memory store spilled | `services/storage/piece-store/piece-disk-store.js` | **nothing at all** |
|
|
11
11
|
| diagnostics kept on purpose | `/data` — core dumps, heap snapshots, packet captures | a count of files, never a size |
|
|
12
12
|
|
|
13
13
|
The middle row is what it cost: one fifty-minute viewing wrote **14 400 MB** to
|
|
@@ -159,3 +159,109 @@ distribution.
|
|
|
159
159
|
**The diagnostics are not a claimant yet.** They are bounded by a count of files
|
|
160
160
|
and never by a size, and they cannot simply be thrown away when space is short: a
|
|
161
161
|
dump is the only evidence of the death it records. That needs a rule of its own.
|
|
162
|
+
|
|
163
|
+
## One layer, four directories, and under the import rule
|
|
164
|
+
|
|
165
|
+
`SegmentStore` moved out of `services/encode/` on 2026-09-14 — a store of
|
|
166
|
+
finished pieces inside the ENCODING layer was the encoder owning its own disk.
|
|
167
|
+
It did not move into the room's own directory either: it is a CLAIMANT, and a
|
|
168
|
+
claimant inside the owner of the resource is the same fault one floor down.
|
|
169
|
+
|
|
170
|
+
**Four directories, deliberately.** They share one property — bytes on a medium
|
|
171
|
+
with a limit — and that is what makes them one layer. What they are is three
|
|
172
|
+
different things: a torrent piece is born when it is downloaded and addressed by
|
|
173
|
+
`(infohash, number)`; a produced segment is born when an encoder closes it and
|
|
174
|
+
addressed by `(output key, number)`; a whole file is born when its last piece
|
|
175
|
+
lands. Different birth, different death, different name, and two of the three
|
|
176
|
+
live in the torrent WORKER thread while the others are on the main one. Flatten
|
|
177
|
+
them and that thread boundary becomes invisible: code that cannot call code
|
|
178
|
+
looks like a sibling, and no rule catches it.
|
|
179
|
+
|
|
180
|
+
**The rule is written as an exception, not as a list.** `group: ["../**",
|
|
181
|
+
"!../storage/**", "!../piece-store/**", "!../segment-store/**", "!../files/**"]`
|
|
182
|
+
— everything outside the layer, minus the layer. A list of forbidden layers was
|
|
183
|
+
tried first and let `../../utils/logger.js` straight through, because a list
|
|
184
|
+
only catches what somebody remembered to write in it. Checked by breaking it,
|
|
185
|
+
in both directions, in all four directories.
|
|
186
|
+
|
|
187
|
+
**One reader of how much memory is free** (`machine-memory.js`). It had been
|
|
188
|
+
written three times — the health collector, the memory report and the piece
|
|
189
|
+
store — and the copies had already drifted: the piece store was corrected from
|
|
190
|
+
`os.freemem()` to the kernel's `MemAvailable` on 2026-08-27 and the health
|
|
191
|
+
collector went on publishing the wrong quantity until 2026-09-02.
|
|
192
|
+
|
|
193
|
+
## Every claimant is told its share
|
|
194
|
+
|
|
195
|
+
A CLAIMANT IS WHOEVER HOLDS BYTES, and by 2026-09-14 all of them are registered
|
|
196
|
+
with the owner (`wire.js`): the segments an encoder produced, the pieces the
|
|
197
|
+
memory store spilled, the files downloaded whole, and the evidence. Two of the
|
|
198
|
+
four live on the torrent thread and arrive as a pair of closures over its
|
|
199
|
+
channel.
|
|
200
|
+
|
|
201
|
+
**Whole files had no bound of any kind** until that day — a 2.8 GB film kept
|
|
202
|
+
whole on a host whose disk is often a 32 GB card. They now take a share, and
|
|
203
|
+
over it the whole file nobody has asked for in longest goes. Losing one is not
|
|
204
|
+
losing data: the torrent can fetch it again, and until it does the read falls
|
|
205
|
+
back to the pieces, which is what `piece-from-whole-file.js` exists for. A file
|
|
206
|
+
is also not ASSEMBLED when there is no room for it — writing it and then
|
|
207
|
+
removing it is the same bytes written for nothing.
|
|
208
|
+
|
|
209
|
+
**The torrent pool's own "disk cap" is gone, and it owned nothing.** It capped
|
|
210
|
+
`torrentDownloadedBytes` — a sum over WebTorrent's bitfield — so a piece held
|
|
211
|
+
purely in MEMORY told against a ceiling called disk, while the bytes it meant to
|
|
212
|
+
bound belong to the spill and to the whole files, each of which has an owner.
|
|
213
|
+
Under it, eviction removed whole idle TORRENTS to free bytes that were not
|
|
214
|
+
necessarily on the disk at all.
|
|
215
|
+
|
|
216
|
+
**The evidence is a claimant with a rule of its own** (`Diagnostics.js`), and the
|
|
217
|
+
rule is what makes a bound on it safe:
|
|
218
|
+
|
|
219
|
+
1. over its share, collection STOPS. Nothing already recorded is deleted to make
|
|
220
|
+
room — a dump is the only evidence of the death it records — and a refusal is
|
|
221
|
+
a LINE with the figures, so an investigation that finds nothing can tell "it
|
|
222
|
+
did not happen" from "there was nowhere to put it";
|
|
223
|
+
2. except what is superseded, which is not evidence twice. A capture of a
|
|
224
|
+
connection already captured adds nothing: the wedge does not un-wedge. Field
|
|
225
|
+
2026-09-06, thirteen captures of one wedge in six hours, 74 MB, every one
|
|
226
|
+
saying what the first said;
|
|
227
|
+
3. what it asks for is measured — what it holds plus one more of the largest
|
|
228
|
+
kind seen — so the claim grows with the evidence and a proxy that has never
|
|
229
|
+
faulted asks for nothing.
|
|
230
|
+
|
|
231
|
+
**Core dumps are not ours to refuse, and that is stated rather than papered
|
|
232
|
+
over.** The kernel writes them, whole address space at a time, and no gate of
|
|
233
|
+
ours is consulted. They are counted, so they take room from what the product may
|
|
234
|
+
hold and the figure is visible; the pruning that keeps the newest two is left
|
|
235
|
+
where it is, because changing it is a decision about evidence and not about
|
|
236
|
+
disk.
|
|
237
|
+
|
|
238
|
+
## One budget
|
|
239
|
+
|
|
240
|
+
There is one owner, one policy and one place that reads the machine —
|
|
241
|
+
`MachineBudget.js`. What it divides is divided PER RESOURCE, because memory
|
|
242
|
+
cannot be paid for with disk, and "disk" is one resource per DEVICE: measured on
|
|
243
|
+
the addon host 2026-09-05, `/tmp` (the segments and the spill) is the overlay
|
|
244
|
+
and `/data` (the evidence) is ext4 on the nvme. Dividing one figure between
|
|
245
|
+
claimants on both gave each a share of a disk it does not write to, which is
|
|
246
|
+
what the old `DiskSpace` did — it read the free space of the segment root alone.
|
|
247
|
+
The device is read with `statSync(dir).dev`, from the nearest ancestor that
|
|
248
|
+
exists, for the same reason `freeBytesFor` walks up.
|
|
249
|
+
|
|
250
|
+
**Why one owner and not two.** The claimants trade across resources: pieces that
|
|
251
|
+
do not fit in memory are spilled to disk, whole files exist so that pieces need
|
|
252
|
+
not be held, segments exist because making them again is dear. Field 2026-08-31:
|
|
253
|
+
14 400 MB spilled in fifty minutes while the memory store held 312-424 MB — give
|
|
254
|
+
it memory and there is no spill to bound. Two owners cannot make that trade,
|
|
255
|
+
because neither sees both sides of it. Memory was divided inside the torrent
|
|
256
|
+
thread and disk on the main one until 2026-09-14; the memory share now travels
|
|
257
|
+
the same channel the spill share already did.
|
|
258
|
+
|
|
259
|
+
**The policy is the operator's, and it is the only chosen number here.**
|
|
260
|
+
`--budget adaptive|share|fixed` with `--budget-share` / `--budget-bytes`, and
|
|
261
|
+
floors with `--min-memory-bytes` / `--min-disk-bytes`. The default is
|
|
262
|
+
`adaptive`, which is the measured rule of `allowance.js` and chooses nothing. A
|
|
263
|
+
floor lifts what the policy allows and can never invent room the machine does
|
|
264
|
+
not have; a claimant left below its own minimum says so in a line, because
|
|
265
|
+
working below what it needs is a fault of the machine and invisible otherwise.
|
|
266
|
+
|
|
267
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Design: Adaptive bitrate (proxy)
|
|
2
2
|
|
|
3
|
-
Read before coding: `services/hwaccel.js` (`chooseSoftwareEncodeSettings`,
|
|
3
|
+
Read before coding: `services/encode/hwaccel.js` (`chooseSoftwareEncodeSettings`,
|
|
4
4
|
the software descriptor's `buildVideoArgs`), `services/hls-session-manager.js`
|
|
5
5
|
(the `BUDGET_*` constants, `#chooseEncodeBudget`, the budget interval check
|
|
6
6
|
`#enforceRealtimeBudget`, `#classifyTranscodeBound`, `#applyBudgetDownshift`,
|
|
@@ -28,7 +28,7 @@ constrained-CRF). Do NOT touch hardware descriptors in this change.
|
|
|
28
28
|
## (b) Net report intake
|
|
29
29
|
|
|
30
30
|
Route: `POST /api/transcode-sessions/:id/net-report` (data-channel path, so
|
|
31
|
-
add the prefix to the allowlist regex in `data-channel-handler.js` if not
|
|
31
|
+
add the prefix to the allowlist regex in `transport/data-channel-handler.js` if not
|
|
32
32
|
covered by the existing `/api/` rule — verify). Body:
|
|
33
33
|
|
|
34
34
|
{ linkMbps: number, bufferedAheadSec: number }
|
|
@@ -46,7 +46,7 @@ trigger is skipped.
|
|
|
46
46
|
|
|
47
47
|
## Impact
|
|
48
48
|
|
|
49
|
-
- `services/hwaccel.js` — nominal-rate table + caps in the software
|
|
49
|
+
- `services/encode/hwaccel.js` — nominal-rate table + caps in the software
|
|
50
50
|
descriptor's video args.
|
|
51
51
|
- `services/hls-session-manager.js` — accept/store net reports per session;
|
|
52
52
|
link trigger in the budget check; log line.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## 1. Caps
|
|
4
4
|
|
|
5
|
-
- [x] 1.1 `hwaccel.js`: `RUNG_NOMINAL_KBPS` table + nearest-rung lookup;
|
|
5
|
+
- [x] 1.1 `encode/hwaccel.js`: `RUNG_NOMINAL_KBPS` table + nearest-rung lookup;
|
|
6
6
|
software descriptor's video args emit `-maxrate`/`-bufsize`
|
|
7
7
|
(1.3× / 1.5× nominal for the actual encode height). Hardware
|
|
8
8
|
descriptors untouched.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Written to be executed as specified — the wire format, constants, limits
|
|
4
4
|
and do-NOT list are normative. Read before coding:
|
|
5
5
|
|
|
6
|
-
- `services/data-channel-handler.js`: the whole file — the wire-protocol
|
|
6
|
+
- `services/transport/data-channel-handler.js`: the whole file — the wire-protocol
|
|
7
7
|
doc comment (~lines 9–37), `handleChannel` (onMessage string handling),
|
|
8
8
|
`handleRequest`, `sendChunk` (the response frame writer whose layout the
|
|
9
9
|
request frames mirror), `send`.
|
|
@@ -46,7 +46,7 @@ the server repo's `chunked-request-bodies` change. Release order: proxy
|
|
|
46
46
|
|
|
47
47
|
## Impact
|
|
48
48
|
|
|
49
|
-
- `services/data-channel-handler.js` — binary inbound frame parsing;
|
|
49
|
+
- `services/transport/data-channel-handler.js` — binary inbound frame parsing;
|
|
50
50
|
per-channel partial-body assembly with caps/TTL; `request-start`
|
|
51
51
|
handling; shared execution path.
|
|
52
52
|
- Release: folds into the PENDING proxy 2.9.35 (extend its CHANGELOG entry;
|
|
@@ -5,7 +5,7 @@ its top first.
|
|
|
5
5
|
|
|
6
6
|
## 1. Implementation
|
|
7
7
|
|
|
8
|
-
- [ ] 1.1 `data-channel-handler.js`: factor the execution tail of
|
|
8
|
+
- [ ] 1.1 `transport/data-channel-handler.js`: factor the execution tail of
|
|
9
9
|
`handleRequest` so a legacy `request` message and an assembled
|
|
10
10
|
chunked request run the SAME function. `node --check`.
|
|
11
11
|
- [ ] 1.2 `request-start` + binary inbound frames + per-channel assembly
|
|
@@ -11,7 +11,7 @@ Written to be executed as specified. Read before coding:
|
|
|
11
11
|
~854), the segment-ready path (where segment 00000 first becomes
|
|
12
12
|
servable — grep the long-poll the `routes/transcode/session-file/get.js`
|
|
13
13
|
route uses).
|
|
14
|
-
- `services/torrent-pool.js`: `prefetchFileEdges` (~line 608) — signature
|
|
14
|
+
- `services/torrent/torrent-pool.js`: `prefetchFileEdges` (~line 608) — signature
|
|
15
15
|
`(torrent, fileIndex, { headBytes, tailBytes, timeoutMs })`.
|
|
16
16
|
- `server.js`: how `playbackPlanner` and `hlsSessionManager` are constructed
|
|
17
17
|
and injected.
|
|
@@ -32,6 +32,6 @@ Assistant itself. This adds the last missing Level 1 piece: a global cap.
|
|
|
32
32
|
|
|
33
33
|
## Impact
|
|
34
34
|
|
|
35
|
-
- `services/torrent-pool.js` (access tracking, cap enforcement),
|
|
35
|
+
- `services/torrent/torrent-pool.js` (access tracking, cap enforcement),
|
|
36
36
|
`bin/cli.js` (`--max-disk-bytes`), `server.js` (option pass-through);
|
|
37
37
|
ha-addon bump. Part of the proxy transcode/hygiene batch (2.9.29).
|
|
@@ -42,8 +42,8 @@ failures:
|
|
|
42
42
|
|
|
43
43
|
- `routes/healthz/get.js`, `routes/health/get.js` (or their handlers) —
|
|
44
44
|
version field.
|
|
45
|
-
- `services/torrent-pool.js` — warning/announce logging, stats origin tags.
|
|
46
|
-
- `services/port-mapper.js` — SSDP listener fix.
|
|
45
|
+
- `services/torrent/torrent-pool.js` — warning/announce logging, stats origin tags.
|
|
46
|
+
- `services/transport/port-mapper.js` — SSDP listener fix.
|
|
47
47
|
- CHANGELOG entry at current package.json version + 1 patch (2.9.25);
|
|
48
48
|
requires the usual ha-addon version bump (0.2.47) after `npm run patch`
|
|
49
49
|
(npm publish needs the user's npm key).
|
|
@@ -40,6 +40,6 @@ subtitle streams were unreachable by the browser.
|
|
|
40
40
|
|
|
41
41
|
- `services/playback-planner.js`, `services/hls-session-manager.js`,
|
|
42
42
|
`routes/api/transcode-sessions/post.js`, new `routes/api/subtitles/get.js`,
|
|
43
|
-
`server.js` wiring, `services/torrent-pool.js` (log masking).
|
|
43
|
+
`server.js` wiring, `services/torrent/torrent-pool.js` (log masking).
|
|
44
44
|
- Pairs with the server-side `track-selection-ui` change; requires the usual
|
|
45
45
|
ha-addon bump.
|
|
@@ -44,7 +44,7 @@ manual-quality menu also needs the server UI).
|
|
|
44
44
|
|
|
45
45
|
## Impact
|
|
46
46
|
|
|
47
|
-
- `services/hwaccel.js` (fps, benchmark, tonemap args),
|
|
47
|
+
- `services/encode/hwaccel.js` (fps, benchmark, tonemap args),
|
|
48
48
|
`services/hls-session-manager.js` (fps probe, runtime speed watch),
|
|
49
49
|
`services/playback-planner.js` (HDR/fps in the plan);
|
|
50
50
|
server player UI for the Quality menu; ha-addon bump.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@torrent-tv/proxy",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.87.0",
|
|
4
4
|
"description": "Torrent proxy client that exposes webseed-like HTTP stream endpoint.",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
6
|
"publishConfig": {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
},
|
|
9
9
|
"type": "module",
|
|
10
10
|
"engines": {
|
|
11
|
-
"node": ">=24
|
|
11
|
+
"node": ">=24"
|
|
12
12
|
},
|
|
13
13
|
"bin": {
|
|
14
14
|
"torrent-tv-proxy": "./bin/cli.js"
|