@torrent-tv/proxy 2.21.1 → 2.22.0

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,8 @@
1
+ ## 2.22.0
2
+
3
+ - **Fix**: What a rung is OFFERED on is the startup measurement again, not the figure learned from a live session. The startup one is taken on a quiet machine against known clips and does not move; the learned one moves with whatever else the box was doing that second, and three field sessions in a row show the price of that: decoding learned at 0.87x, then at 1.34-1.57x, against calibration's 2.6x — each reading refusing another rung until the offer held a single height and the quality menu vanished with it.
4
+ - **New**: A live reading keeps the one thing it is authority on — itself. A rung that has actually been seen running below realtime, with the machine to itself, is withdrawn on that evidence whatever any prediction says. A rung nobody has run is judged by the startup measurement like any other, because a measurement of one rung is not a prediction about the rest.
5
+
1
6
  ## 2.21.1
2
7
 
3
8
  - **Fix**: A cost is learned only from an encoder that had the machine to itself. Beside another encoder a reading already contains that other work, and the budget then ADDS the same work again when it predicts — so the price of a file grew with every reading. Measured in the field 2026-08-15: copying, whose truth is 7.9x, was learned as 2.03x; decoding, whose calibration clips say 2.6x, as 0.87x. Every re-encoded rung was then refused (`not offering 720p=0.56x … 240p=0.66x`), the offer collapsed to the one copied height, and the viewer lost the quality menu entirely.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@torrent-tv/proxy",
3
- "version": "2.21.1",
3
+ "version": "2.22.0",
4
4
  "description": "Torrent proxy client that exposes webseed-like HTTP stream endpoint.",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "publishConfig": {
@@ -4750,6 +4750,9 @@ export class HlsSessionManager {
4750
4750
  heights: ordered,
4751
4751
  ownHeight: own,
4752
4752
  playingHeight: playing,
4753
+ // What each rung was actually seen doing in this session, which is the
4754
+ // only thing a live reading may speak for.
4755
+ measuredHeights: this.#measuredRungSpeeds(owner),
4753
4756
  // What the family is already spending while a rung is considered. The
4754
4757
  // picture being COPIED is the common case and used to be priced at
4755
4758
  // nothing; measured, it is about an eighth of the machine.
@@ -4759,7 +4762,18 @@ export class HlsSessionManager {
4759
4762
  fps: Number(owner.outputFps) || TRANSCODE_FPS,
4760
4763
  source: owner.sourceDecode ?? null,
4761
4764
  transcodeVideo: owner.transcodeVideo === true,
4762
- observedDecodeCostSec: observed?.costSec ?? null
4765
+ // NOT the learned cost. What a rung is OFFERED on is the startup
4766
+ // measurement, which is taken on a quiet machine against known clips and
4767
+ // does not move; the figure learned from a live session moves with
4768
+ // whatever else the box was doing at that second, and three field
4769
+ // sessions in a row (2026-08-15) show what that costs: 0.87x, then
4770
+ // 1.34-1.57x against calibration's 2.6x, each reading refusing another
4771
+ // rung until the offer held one height and the menu disappeared with it.
4772
+ //
4773
+ // The learned figure keeps its job — but only over the rung it was
4774
+ // measured ON, and only to take that one away (below). A measurement of
4775
+ // one rung is not a prediction about the others.
4776
+ observedDecodeCostSec: null
4763
4777
  });
4764
4778
  if (owner !== session) {
4765
4779
  // An orphan: its base is gone, so this is the family's last word and
@@ -4896,6 +4910,10 @@ export class HlsSessionManager {
4896
4910
  if (this.#runningEncoders() > 1) {
4897
4911
  return;
4898
4912
  }
4913
+ // What this rung did with the machine to itself — the one figure a live
4914
+ // reading is authority on, and what withdraws a rung that has been seen
4915
+ // failing without letting it speak for rungs nobody has run.
4916
+ session.lastAloneSpeed = speed;
4899
4917
  if (speed < BUDGET_SPEED_OK && await this.#classifyTranscodeBound(session) === "download") {
4900
4918
  return; // the torrent is what is short; this says nothing about the host
4901
4919
  }
@@ -5158,6 +5176,32 @@ export class HlsSessionManager {
5158
5176
  return cost;
5159
5177
  }
5160
5178
 
5179
+ /**
5180
+ * The speed each rung of this family was last seen running at, when it was
5181
+ * running alone.
5182
+ *
5183
+ * A rung that has been watched failing is refused on that evidence; a rung
5184
+ * nobody has run says nothing about itself and is judged by the startup
5185
+ * measurement like any other.
5186
+ *
5187
+ * @param {HlsSession} base
5188
+ * @returns {Map<number, number>}
5189
+ */
5190
+ #measuredRungSpeeds(base) {
5191
+ /** @type {Map<number, number>} */
5192
+ const speeds = new Map();
5193
+ for (const session of this.#familyOf(base)) {
5194
+ if (session.transcodeVideo !== true || !Number.isFinite(session.lastAloneSpeed)) {
5195
+ continue;
5196
+ }
5197
+ const height = this.variantHeightOf(session);
5198
+ if (height > 0) {
5199
+ speeds.set(height, session.lastAloneSpeed);
5200
+ }
5201
+ }
5202
+ return speeds;
5203
+ }
5204
+
5161
5205
  #sustainableHeights({
5162
5206
  heights,
5163
5207
  ownHeight,
@@ -5168,7 +5212,8 @@ export class HlsSessionManager {
5168
5212
  source,
5169
5213
  transcodeVideo,
5170
5214
  observedDecodeCostSec = null,
5171
- concurrentCostSec = 0
5215
+ concurrentCostSec = 0,
5216
+ measuredHeights = null
5172
5217
  }) {
5173
5218
  const benchmark = this.softwarePresetBenchmark;
5174
5219
  if (!Array.isArray(benchmark) || benchmark.length === 0 || sourceHeight <= 0 || sourceWidth <= 0) {
@@ -5200,6 +5245,14 @@ export class HlsSessionManager {
5200
5245
  kept.push(height);
5201
5246
  continue;
5202
5247
  }
5248
+ // A rung this session has actually been seen running below realtime is
5249
+ // withdrawn on that evidence, whatever the prediction says. This is the
5250
+ // one thing a live reading is authority on: itself.
5251
+ const measured = measuredHeights?.get(height) ?? null;
5252
+ if (measured !== null && measured < 1) {
5253
+ dropped.push(`${height}p=${measured.toFixed(2)}x measured`);
5254
+ continue;
5255
+ }
5203
5256
  const width = Math.round(((sourceWidth / sourceHeight) * height) / 2) * 2;
5204
5257
  const { speed, sustainable } = canSustainOutput({
5205
5258
  benchmark,