@torrent-tv/proxy 2.99.1 → 2.99.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@torrent-tv/proxy",
3
- "version": "2.99.1",
3
+ "version": "2.99.2",
4
4
  "description": "Torrent proxy client that exposes webseed-like HTTP stream endpoint.",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "repository": {
@@ -496,7 +496,11 @@ export class QualityController {
496
496
  bufferedSec: buffer?.bufferedSec ?? 0,
497
497
  slope: buffer?.slope ?? null,
498
498
  reportGapSec: buffer?.reportGapSec ?? 0,
499
- secondsToReady: this.#secondsToReady(session)
499
+ secondsToReady: this.#secondsToReady(session),
500
+ // What is left of the film after them: a buffer holding all of it drains
501
+ // because the film ends.
502
+ remainingSec: Number.isFinite(buffer?.positionSec) && session.file?.durationSeconds > 0
503
+ ? Math.max(0, session.file.durationSeconds - buffer.positionSec) : null
500
504
  });
501
505
  if (!threat) {
502
506
  return false;
@@ -20,6 +20,13 @@
20
20
  *
21
21
  * A time to ready that is not known is not assumed to be short: with nothing to
22
22
  * show there is time left, a draining buffer is a threat.
23
+ *
24
+ * A BUFFER HOLDING THE REST OF THE FILM IS NO THREAT: it drains because the
25
+ * film ends, and no output can add to it. Field r43 (Home Assistant,
26
+ * 2026-10-15): the last two minutes of an AVI were read as a draining buffer,
27
+ * a faster setting was prepared 53 times and given up each time with nothing
28
+ * left to give, and the player was asked for 240p eight times
29
+ * (torrent-tv/meta#178).
23
30
  */
24
31
 
25
32
  /**
@@ -31,14 +38,19 @@
31
38
  * two reports.
32
39
  * @param {number | null} params.secondsToReady - How long the new output would
33
40
  * take to close the piece the viewer needs; null when not known.
41
+ * @param {number | null} [params.remainingSec] - The film left after where the
42
+ * viewer stands; null when not known.
34
43
  * @returns {{ threat: boolean, secondsToEmpty: number | null }}
35
44
  */
36
- export function drainThreat({ bufferedSec, slope, reportGapSec, secondsToReady }) {
45
+ export function drainThreat({ bufferedSec, slope, reportGapSec, secondsToReady, remainingSec = null }) {
37
46
  if (!Number.isFinite(slope) || slope >= 0) {
38
47
  return { threat: false, secondsToEmpty: null };
39
48
  }
40
49
  const held = Number.isFinite(bufferedSec) && bufferedSec > 0 ? bufferedSec : 0;
41
50
  const secondsToEmpty = held / -slope;
51
+ if (Number.isFinite(remainingSec) && held >= remainingSec) {
52
+ return { threat: false, secondsToEmpty };
53
+ }
42
54
  if (!Number.isFinite(secondsToReady) || secondsToReady < 0) {
43
55
  return { threat: true, secondsToEmpty };
44
56
  }
@@ -338,7 +338,8 @@ export function bufferOf(viewers, output, consumerId, spanSec) {
338
338
  }
339
339
  const trend = viewer.bufferTrend(spanSec);
340
340
  const held = Number.isFinite(viewer.bufferedSeconds) ? Math.max(0, viewer.bufferedSeconds) : 0;
341
- return { bufferedSec: held, slope: trend?.slope ?? null, reportGapSec: trend?.reportGapSec ?? 0 };
341
+ return { bufferedSec: held, slope: trend?.slope ?? null, reportGapSec: trend?.reportGapSec ?? 0,
342
+ positionSec: viewer.positionSeconds() ?? null };
342
343
  }
343
344
 
344
345
  /**