@torrent-tv/proxy 2.64.4 → 2.64.5

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.64.5
2
+
3
+ - **Fix**: A rung measured at 0.007x is no longer kept just because it is on screen. The `playingHeight` exemption is now checked after the `measured < 1` withdrawal, so a 4K HEVC transcode at 0.007x on a CM4 (field 2026-08-31, 0.1x at 23:45 and 0.007x at 06:57, 0.04s buffered) is withdrawn and the offer can become empty instead of stalling the viewer with no way to downgrade. `ownHeight` is kept only for a copied source (`!transcodeVideo`), not for a re-encode already running at 0.18x.
4
+
1
5
  ## 2.64.4
2
6
 
3
7
  - **Fix**: Piece store holds one `SharedArrayBuffer` per resident piece instead of a single growable pool that only ever grew. Evicting a piece deletes its buffer and the memory is reclaimable by GC — `committed` is now `resident * chunkLength`, not a high-water. On the field host a 6-piece 8 MiB overflow kept 48 MB committed; now it keeps 32 MB (roadmap 2).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@torrent-tv/proxy",
3
- "version": "2.64.4",
3
+ "version": "2.64.5",
4
4
  "description": "Torrent proxy client that exposes webseed-like HTTP stream endpoint.",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "publishConfig": {
@@ -7529,32 +7529,35 @@ export class HlsSessionManager {
7529
7529
  /** @type {Map<number, number | null>} */
7530
7530
  const predictedByHeight = new Map();
7531
7531
  for (const height of heights) {
7532
- // The rung ON SCREEN is never withdrawn, whatever anything says about it:
7533
- // every route guard reads this list, so dropping it would 404 the next
7534
- // segment of a stream that is playing.
7535
- if (height === playingHeight) {
7536
- kept.push(height);
7537
- continue;
7538
- }
7539
7532
  // A rung this session has actually been seen running below realtime is
7540
7533
  // withdrawn on that evidence, whatever the prediction says. This is the
7541
- // one thing a live reading is authority on: itself and it is asked
7542
- // BEFORE the exemptions below, which it did not used to be.
7543
- //
7544
- // That order was harmless while a step rewrote the encode inside the base
7545
- // and the viewer never left it: `ownHeight` then always WAS the rung on
7546
- // screen. Now a step moves the viewer to another variant, and the height
7547
- // they left goes on being exempt although nothing is producing it so
7548
- // the step back up would ask for the one rung this machine has been
7549
- // measured failing at, then fail again, then step down, for ever. The
7550
- // copied source height cannot reach this: `#measuredRungSpeeds` records
7551
- // only sessions that re-encode, so a copy has no reading to be withdrawn
7552
- // on, which is right — it costs no encoder.
7534
+ // one thing a live reading is authority on: itself. It is asked before
7535
+ // any exemption so a rung measured failing while on screen does not stay
7536
+ // offered because it was on screen when measured — otherwise a step
7537
+ // would ask for the one rung this machine has been measured failing at,
7538
+ // then fail again, then step down, for ever. A copied source height
7539
+ // cannot reach this: `#measuredRungSpeeds` records only sessions that
7540
+ // re-encode, so a copy has no reading to be withdrawn on, which is right
7541
+ // it costs no encoder.
7553
7542
  const measured = measuredHeights?.get(height) ?? null;
7554
7543
  if (measured !== null && measured < 1) {
7544
+ // Even the rung on screen is withdrawn on measured failure: keeping it
7545
+ // would 404 the next segment, but keeping a rung measured at 0.007x
7546
+ // (field 2026-08-31, 4K HEVC on CM4) stalls the viewer for minutes with
7547
+ // 0.04s buffered and no way to downgrade because every other rung is
7548
+ // also dropped. Withdrawing it lets the offer become empty, which the
7549
+ // caller turns into an error the viewer can act on (try another proxy
7550
+ // or a lower source) instead of an endless spinner.
7555
7551
  dropped.push(`${height}p=${measured.toFixed(2)}x measured`);
7556
7552
  continue;
7557
7553
  }
7554
+ // The rung ON SCREEN is kept only when it has not been measured failing
7555
+ // above. Keeping a rung measured at 0.007x would stall the viewer with
7556
+ // no path to a faster rung, which is what the field showed.
7557
+ if (height === playingHeight) {
7558
+ kept.push(height);
7559
+ continue;
7560
+ }
7558
7561
  // The height an encoder is ALREADY producing, and the source's own height
7559
7562
  // when the FAMILY serves it by copy — neither has to be predicted,
7560
7563
  // because it is happening. A copied rung costs no encoder at all, so no
@@ -7567,9 +7570,13 @@ export class HlsSessionManager {
7567
7570
  // A source height that would have to be RE-ENCODED is a prediction like
7568
7571
  // any other: on a session whose budget stepped down to 480p, the source's
7569
7572
  // 1080p is neither copied nor being produced, and keeping it unpriced
7570
- // would offer exactly the kind of rung this refuses.
7573
+ // would offer exactly the kind of rung this refuses. Likewise, a rung
7574
+ // this session is already producing at 0.007x (field 2026-08-31, 4K HEVC
7575
+ // on CM4, 0.1x at 23:45 and 0.007x at 06:57) is not sustainable just
7576
+ // because it is running — keeping it offered no path to a faster rung
7577
+ // and left the viewer at 0.04s buffered with no downgrade.
7571
7578
  if (
7572
- height === ownHeight ||
7579
+ (height === ownHeight && !transcodeVideo) ||
7573
7580
  (height === sourceHeight && !transcodeVideo)
7574
7581
  ) {
7575
7582
  kept.push(height);