@torrent-tv/proxy 2.101.8 → 2.101.9

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.101.8",
3
+ "version": "2.101.9",
4
4
  "description": "Torrent proxy client that exposes webseed-like HTTP stream endpoint.",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "repository": {
@@ -633,8 +633,10 @@ export class Renditions {
633
633
  }
634
634
  const bar = this.#host.requiredSpeedOf?.(base) ?? 1;
635
635
  const priced = [];
636
+ const speeds = new Map();
636
637
  const holds = (preset) => {
637
638
  const speed = this.#host.presetSpeedOf?.(current, preset) ?? null;
639
+ speeds.set(preset, speed);
638
640
  priced.push(`${preset}=${speed ? `${speed.effective.toFixed(2)}x` : "unpriced"}`);
639
641
  return speed !== null && speed.effective >= bar;
640
642
  };
@@ -654,6 +656,7 @@ export class Renditions {
654
656
  return this.#beginMove({
655
657
  base, current, consumerId, askedHeights, direction, changes: "preset",
656
658
  reasonText: `${reasonText}; priced ${priced.join(", ")} against ${bar.toFixed(2)}x`,
659
+ speed: speeds.get(target)?.processing ?? null,
657
660
  opening: this.#stepOpening(base, {
658
661
  height: encode.height,
659
662
  startSeconds: this.#host.viewerPositionOf(base.id, consumerId),
@@ -679,7 +682,8 @@ export class Renditions {
679
682
  * @param {object} move
680
683
  * @returns {Promise<{ started: boolean, reason: string, noPlace?: boolean }>}
681
684
  */
682
- async #beginMove({ base, current, consumerId, askedHeights, direction, changes, reasonText, opening, what, matches, described }) {
685
+ async #beginMove({ base, current, consumerId, askedHeights, direction, changes, reasonText, opening, what, matches, described,
686
+ speed = null }) {
683
687
  const refuse = (reason) => ({ started: false, reason });
684
688
  let prepared;
685
689
  try {
@@ -728,6 +732,11 @@ export class Renditions {
728
732
  prepared.variantHeight ??= current.spec.video.encode.height;
729
733
  this.#host.outputs.markStep(prepared);
730
734
  }
735
+ // The speed this move was decided at is what the start forecast reads for
736
+ // the output until a piece of it is measured (torrent-tv/meta#185).
737
+ if (Number.isFinite(speed) && speed > 0) {
738
+ this.#host.notePredictedSpeed?.(prepared, speed);
739
+ }
731
740
  this.#host.noteSameHeightSwitch(consumerId, {
732
741
  askedHeights,
733
742
  outputId: prepared.id,
@@ -128,6 +128,33 @@ export class EncodeCost {
128
128
  }
129
129
  }
130
130
 
131
+ /**
132
+ * The speed a move onto another setting of this output's encoder was decided
133
+ * at (`presetSpeedOf`, its processing): what this output stands on until a
134
+ * piece of it is measured, instead of the figure taken when it was opened.
135
+ * That figure counts the output the move replaces as running beside it.
136
+ * Field r82 (torrent-tv/meta#185): an output at `faster` opened during a cold
137
+ * start was predicted 0.22x, its move was priced 1.81x, it measured
138
+ * 1.46-1.65x, and the start forecast on 0.22x stated 30 596 s where the
139
+ * picture came in 10.5 s. The price assumes the output on screen stops; where
140
+ * another viewer keeps it running, the figure is high until a piece is
141
+ * measured. A move is not an offer, so no cheapest-mode figure stands beside
142
+ * it.
143
+ *
144
+ * @param {object} session - The output the move is prepared onto.
145
+ * @param {number} speed - Seconds of film per second.
146
+ * @returns {void}
147
+ */
148
+ notePriceFor(session, speed) {
149
+ const preset = session?.spec?.video?.encode?.preset ?? null;
150
+ if (!session?.spec?.transcodesVideo || !preset || !Number.isFinite(speed) || speed <= 0) return;
151
+ const state = qualityStateOf(session);
152
+ state.predictedSpeedWhenOffered = speed;
153
+ state.predictedMode = preset;
154
+ state.offeredSpeedAtCheapestMode = null;
155
+ state.lastPredictionRatio = null;
156
+ }
157
+
131
158
  /**
132
159
  * What this output's picture would do at another speed setting of its
133
160
  * encoder, at the same size, on this machine as it is: the startup
@@ -456,6 +456,14 @@ export function wireOutputs({
456
456
  presetLadder: () => (parts.encoders.current?.kind === "software" && Array.isArray(parts.softwarePresetBenchmark)
457
457
  ? parts.softwarePresetBenchmark.map((entry) => entry.preset) : []),
458
458
  presetSpeedOf: (output, preset) => parts.encodeCost.presetSpeedOf(output, preset, { segmentSeconds: parts.segmentDurationSec }),
459
+ // The price a move was decided at, given to an output with no measured
460
+ // piece: one already measured stands on its readings, and an output that
461
+ // runs was priced with its own cost counted beside the one on screen.
462
+ notePredictedSpeed: (output, speed) => {
463
+ if (parts.encodeRuns.speedReadingsOf(output).length === 0 && parts.encodeRuns.liveRunsOf(output).length === 0) {
464
+ parts.encodeCost.notePriceFor(output, speed);
465
+ }
466
+ },
459
467
  requiredSpeedOf: (output) => parts.hostLoad.requiredSpeedFor(output.file.sourceKey, output.file.fileIndex) ?? 1,
460
468
  heightsChosenAs: (consumerId, outputKey) => heightsChosenAs(parts.viewers, consumerId, outputKey),
461
469
  highestGivenSegmentOf: (consumerId, askedHeight) => highestGivenSegmentOf(parts.viewers, consumerId, askedHeight),