@torrent-tv/proxy 2.9.69 → 2.9.71

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 CHANGED
@@ -1,3 +1,13 @@
1
+ ## 2.9.71
2
+
3
+ - **New**: The torrent client now runs on its own thread (`services/torrent-worker/`). Profiling a live seek (2026-08-02) found the main thread ~85% occupied by WebTorrent — buffer concatenation in `uint8-util` ~15%, `_updateWire` and its wrapper ~9%, garbage collection ~5%, and **no piece hashing at all**, which had been the standing assumption — while three of four cores idled. Serving a segment shared that thread, so reading an already-finished 10 MB file off SSD took **12-23 s** where handing it to the channel took 125 ms. Measured after the split, through the real `/stream` route: **3 MB in 0.05-0.12 s** (~500 Mbps), roughly a hundredfold improvement, with main-thread event-loop delay down from 300-390 ms to **28-38 ms**.
4
+ - **Chore**: The transport was chosen by measurement, not preference. A 10 MB body costs 37 ms structured-cloned, **104 ms through a transferable `ReadableStream`** (the obvious standard answer, and 22x worse), and **4.8-5.3 ms** transferring ownership of 1 MB chunks behind an ordinary `ReadableStream` wrapper — standard interface outside, ownership transfer inside, which is what shipped. Chunk size follows the same arithmetic: at ~100 µs per round trip, 64 KB chunks would spend 13 ms per segment on overhead versus ~1 ms at 1 MB. Backpressure caps chunks in flight so a fast disk cannot rebuild in the message queue the memory the transfers save.
5
+ - **Chore**: `WorkerTorrentPool` presents `TorrentPool`'s existing interface, so the switch is one line in `server.js` and none of the twelve call sites across the stream route, subtitle route, playback planner and health report changed. Torrent objects cannot cross a thread, so the worker keys them by `sourceKey` and hands back a stand-in exposing the `files[i].createReadStream()` shape callers already use.
6
+
7
+ ## 2.9.70
8
+
9
+ - **Chore**: Instrumentation to settle where a slow transfer actually loses its time, instead of arguing about it. Every data-channel body transfer now reports the split — `readMs` (reading the body from the local route), `chanMs` (handing chunks to the channel), `drainMs` (waiting for the channel queue) — plus `rate` and, decisively, the **event-loop delay** over the same window (`loopMean`/`loopP99`/`loopMax`, via `perf_hooks.monitorEventLoopDelay`). Synchronous work blocking the loop looks exactly like a slow network from the outside; these figures tell them apart. Prompted by a field seek where a 9.4 MB segment took 16.5 s to deliver with the channel queue **empty the whole time** (`maxBuffered=0`) while the encoder ran at 14x realtime and the file was already on disk — so none of encoder, torrent or channel capacity explained it, and no measurement existed that could. New `utils/perf.js` (`OperationTimer`, `eventLoopDelay`); deeper tools (`--trace-events-enabled`, `--cpu-prof`) remain for when these point somewhere specific.
10
+
1
11
  ## 2.9.69
2
12
 
3
13
  - **Fix**: Removed the last traces of the seek-start "pull", so nothing can move the encode position except the viewer's own seek. Root cause now measured rather than guessed: **during a scrub the player loads from wherever the slider pauses on its way**. Browser log 2026-08-02 — dragging from 0 to 23:34 lingered at 863.4 s, the player fetched segment #82 for that intermediate point, and a seek that had correctly resolved to start at #134 was dragged back to **#82**, then crawled forward for a minute. The browser's 300 ms debounce exists precisely to discard intermediate scrub positions; reading them back off the segment-request stream defeated it. Gone with it: `lowestAwaitedIndex` tracking, `SEEK_PULL_LIMIT_SEGMENTS`, and the reset paths they needed.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@torrent-tv/proxy",
3
- "version": "2.9.69",
3
+ "version": "2.9.71",
4
4
  "description": "Torrent proxy client that exposes webseed-like HTTP stream endpoint.",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "publishConfig": {
package/server.js CHANGED
@@ -29,7 +29,7 @@ import { handleApiTranscodeSessionSeekPost } from "./routes/api/transcode-sessio
29
29
  import { handleStreamGet } from "./routes/stream/get.js";
30
30
  import { handleTranscodeSessionFileGet } from "./routes/transcode/session-file/get.js";
31
31
  import { createSourceRegistry } from "./store/source-registry.js";
32
- import { TorrentPool } from "./services/torrent-pool.js";
32
+ import { WorkerTorrentPool } from "./services/torrent-worker/pool-adapter.js";
33
33
  import { HlsSessionManager } from "./services/hls-session-manager.js";
34
34
  import { createPlaybackPlanner } from "./services/playback-planner.js";
35
35
  import { detectVideoEncoder, benchmarkSoftwarePresets, detectTonemapSupport } from "./services/hwaccel.js";
@@ -98,7 +98,13 @@ export async function startProxyServer({ host, port, transcodeAudio, ffmpegBin,
98
98
  });
99
99
 
100
100
  const sourceRegistry = createSourceRegistry(200);
101
- const torrentPool = new TorrentPool({ maxDiskBytes });
101
+ // The torrent runs on its own thread. Profiling a live seek (2026-08-02)
102
+ // found the main thread ~85% occupied by WebTorrent — buffer concatenation
103
+ // ~15%, wire updates ~9%, garbage collection ~5% — while three of four cores
104
+ // idled. Serving a segment shared that thread, so reading an already-finished
105
+ // 10 MB file took 12-23 s against 125 ms to hand it to the channel. The
106
+ // adapter keeps TorrentPool's interface, so nothing downstream changed.
107
+ const torrentPool = new WorkerTorrentPool({ maxDiskBytes });
102
108
  const selectedPort = await getPort({
103
109
  port: buildPortCandidates(port)
104
110
  });