@torrent-tv/proxy 2.9.67 → 2.9.69

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,11 @@
1
+ ## 2.9.69
2
+
3
+ - **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.
4
+
5
+ ## 2.9.68
6
+
7
+ - **Fix**: A seek could be dragged back to the position the viewer had just left. The encoder start was pulled down to the lowest segment the player had outstanding — a stand-in from when the distance to the preceding keyframe was unknown — but at seek time those requests still describe where the player was PLAYING, not where it is going. Field 2026-08-02: a seek to 23:34 (#135) correctly resolved to a start of #134, then got pulled to **#82** (14:15, the position just left) and crawled forward from there. Removed: since boundaries became real keyframes (2.9.65), exactly one segment back always suffices, so the pull has nothing left to correct for.
8
+
1
9
  ## 2.9.67
2
10
 
3
11
  - **Fix**: A seek waited far longer than it needed to — 56 s measured in the field, of which roughly 50 s was self-inflicted. `SEEK_BACKOFF_SEGMENTS` (how far before the requested segment the encoder starts) was **12**, chosen when segments were an invented 4 s apart and the distance to a usable keyframe was unknown. Since 2.9.65 every boundary IS a real keyframe read from the container index, so the single preceding segment is guaranteed to start on one — and with real 10.43 s segments the old value meant encoding **125 s of content** before reaching the viewer position. Lowered to **1**. Observed: the encoder started at #332 for a seek to #344 and the requested segment only arrived 56 s later, while every segment after it was served in ~100 ms.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@torrent-tv/proxy",
3
- "version": "2.9.67",
3
+ "version": "2.9.69",
4
4
  "description": "Torrent proxy client that exposes webseed-like HTTP stream endpoint.",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "publishConfig": {
@@ -84,7 +84,6 @@ const ENCODER_STALL_MS = 12_000;
84
84
  // encoding 125 s of content before reaching the viewer's position. Field
85
85
  // 2026-08-02: a seek took 56 s, of which ~50 s was this backoff.
86
86
  const SEEK_BACKOFF_SEGMENTS = 1;
87
- const SEEK_PULL_LIMIT_SEGMENTS = 120;
88
87
  const SEEK_SETTLE_MS = 1_200;
89
88
  // Hard cap on the total settle wait, measured from the first far request of a
90
89
  // burst, so a still-moving scrubber cannot delay a genuine seek forever.
@@ -1107,9 +1106,6 @@ export class HlsSessionManager {
1107
1106
  // #wireEncodeProcess and MAX_SEEK_FAILURES.
1108
1107
  seekFailureTarget: -1,
1109
1108
  seekFailureCount: 0,
1110
- // Lowest segment index the player is currently waiting for; -1 when
1111
- // nothing is pending. See getFileStream and #fireSettledSeek.
1112
- lowestAwaitedIndex: -1,
1113
1109
  progress: {
1114
1110
  state: "starting",
1115
1111
  processedSeconds: 0,
@@ -2139,10 +2135,6 @@ export class HlsSessionManager {
2139
2135
  // player needs a segment containing the preceding keyframe, so one that
2140
2136
  // begins exactly at the target is useless to it.
2141
2137
  const startIndex = Math.max(0, index - SEEK_BACKOFF_SEGMENTS);
2142
- // A new seek invalidates everything the player was waiting for before it:
2143
- // those requests describe where it USED to be. Clearing here is what keeps
2144
- // the pull below anchored to this seek.
2145
- session.lowestAwaitedIndex = -1;
2146
2138
  logger.info(
2147
2139
  `transcode ${session.id} viewer seek to ${positionSeconds.toFixed(1)}s → segment #${index}, ` +
2148
2140
  `starting at #${startIndex} (${SEEK_BACKOFF_SEGMENTS} back for the preceding keyframe)`
@@ -2244,23 +2236,23 @@ export class HlsSessionManager {
2244
2236
  : producedThisRun >= this.segmentDurationSec
2245
2237
  ? `run produced ${producedThisRun.toFixed(1)}s (first segment done)`
2246
2238
  : `grace of ${RUN_FIRST_SEGMENT_GRACE_MS / 1000}s expired`;
2247
- // The player may be waiting on something below our fixed backoff — its own
2248
- // requests say exactly how far back it needs the keyframe, so honour that
2249
- // rather than a guess. Only ever pulls the start EARLIER, never later.
2250
- const awaited = session.lowestAwaitedIndex;
2251
- const pullFloor = Math.max(0, target - SEEK_PULL_LIMIT_SEGMENTS);
2252
- const effectiveTarget = awaited >= pullFloor && awaited < target ? awaited : target;
2253
- if (effectiveTarget !== target) {
2254
- logger.info(
2255
- `transcode ${session.id} pulling encode start #${target} #${effectiveTarget} ` +
2256
- `(lowest segment the player is waiting on)`
2257
- );
2258
- }
2239
+ // The start is exactly what requestSeek computed one segment before the
2240
+ // viewer's position and nothing else may move it.
2241
+ //
2242
+ // An earlier version pulled it down to the lowest segment the player had
2243
+ // outstanding, guessing how far back the preceding keyframe lay. That guess
2244
+ // is unnecessary now (boundaries ARE keyframes since 2.9.65) and was
2245
+ // actively wrong: during a scrub the player loads from wherever the slider
2246
+ // paused on its way, so those requests describe INTERMEDIATE positions, not
2247
+ // the destination. Measured 2026-08-02: dragging from 0 to 23:34 paused at
2248
+ // 863.4 s, the player fetched #82 for it, and a seek correctly resolved to
2249
+ // #134 was dragged back to #82. The browser's 300 ms debounce exists to
2250
+ // discard those intermediate positions — reading them back off the request
2251
+ // stream defeated it.
2259
2252
  session.seekTarget = null;
2260
2253
  session.seekFirstFarAt = 0;
2261
- session.lowestAwaitedIndex = -1;
2262
- logger.info(`transcode ${session.id} seek settle → restart at segment #${effectiveTarget} (${allowedBecause})`);
2263
- void this.#startEncodeRun(session, effectiveTarget);
2254
+ logger.info(`transcode ${session.id} seek settle → restart at segment #${target} (${allowedBecause})`);
2255
+ void this.#startEncodeRun(session, target);
2264
2256
  }
2265
2257
 
2266
2258
  /**
@@ -2464,16 +2456,6 @@ export class HlsSessionManager {
2464
2456
  // at this position (server-side seeking). The caller long-polls.
2465
2457
  if (!isPlaylist) {
2466
2458
  const requestedIndex = this.segmentFormat.segmentIndexFromName(fileName);
2467
- // Remember the LOWEST segment currently being waited on. The player
2468
- // always fetches below the seek target (it needs the preceding keyframe),
2469
- // and by how much varies — 8 segments in one measured seek, 57 in
2470
- // another. This is that figure straight from the player, and
2471
- // #fireSettledSeek uses it to pull the encode start down when the fixed
2472
- // SEEK_BACKOFF_SEGMENTS floor is not deep enough. Reset whenever a run
2473
- // starts, so it only ever describes the pending seek.
2474
- if (requestedIndex >= 0 && (session.lowestAwaitedIndex < 0 || requestedIndex < session.lowestAwaitedIndex)) {
2475
- session.lowestAwaitedIndex = requestedIndex;
2476
- }
2477
2459
  this.#ensureEncodingFor(
2478
2460
  session,
2479
2461
  requestedIndex,