@torrent-tv/proxy 2.9.68 → 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,7 @@
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
+
1
5
  ## 2.9.68
2
6
 
3
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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@torrent-tv/proxy",
3
- "version": "2.9.68",
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,20 +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
- // Deliberately NOT pulled towards the lowest segment the player is waiting
2248
- // on. That was a stand-in for not knowing how far back the preceding
2249
- // keyframe lay, and it is now both unnecessary and harmful: boundaries are
2250
- // real keyframes (2.9.65), so exactly one segment back always suffices,
2251
- // while the player's outstanding requests at seek time still describe where
2252
- // it was PLAYING, not where it is going. Field 2026-08-02: a seek to #135
2253
- // was dragged back to #82 the position the viewer had just left — because
2254
- // the player was still fetching around it.
2255
- const effectiveTarget = target;
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.
2256
2252
  session.seekTarget = null;
2257
2253
  session.seekFirstFarAt = 0;
2258
- session.lowestAwaitedIndex = -1;
2259
- logger.info(`transcode ${session.id} seek settle → restart at segment #${effectiveTarget} (${allowedBecause})`);
2260
- void this.#startEncodeRun(session, effectiveTarget);
2254
+ logger.info(`transcode ${session.id} seek settle → restart at segment #${target} (${allowedBecause})`);
2255
+ void this.#startEncodeRun(session, target);
2261
2256
  }
2262
2257
 
2263
2258
  /**
@@ -2461,16 +2456,6 @@ export class HlsSessionManager {
2461
2456
  // at this position (server-side seeking). The caller long-polls.
2462
2457
  if (!isPlaylist) {
2463
2458
  const requestedIndex = this.segmentFormat.segmentIndexFromName(fileName);
2464
- // Remember the LOWEST segment currently being waited on. The player
2465
- // always fetches below the seek target (it needs the preceding keyframe),
2466
- // and by how much varies — 8 segments in one measured seek, 57 in
2467
- // another. This is that figure straight from the player, and
2468
- // #fireSettledSeek uses it to pull the encode start down when the fixed
2469
- // SEEK_BACKOFF_SEGMENTS floor is not deep enough. Reset whenever a run
2470
- // starts, so it only ever describes the pending seek.
2471
- if (requestedIndex >= 0 && (session.lowestAwaitedIndex < 0 || requestedIndex < session.lowestAwaitedIndex)) {
2472
- session.lowestAwaitedIndex = requestedIndex;
2473
- }
2474
2459
  this.#ensureEncodingFor(
2475
2460
  session,
2476
2461
  requestedIndex,