@torrent-tv/proxy 2.21.0 → 2.21.1

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.21.1
2
+
3
+ - **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.
4
+
1
5
  ## 2.21.0
2
6
 
3
7
  - **Fix**: The two costs added in 2.18.0 and 2.19.0 were never measured in production — both features were inert. The torrent's cost read `torrentPool.client`, a field that belongs to the pool implementation that no longer runs on this thread (the WebTorrent client lives on the worker), so the byte totals were always zero and the guard that needs two megabytes of movement never passed. The copy's cost sat in a branch its only caller had already filtered out, so it never ran. The totals now come from the worker over its own protocol, and the caller admits a copying session.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@torrent-tv/proxy",
3
- "version": "2.21.0",
3
+ "version": "2.21.1",
4
4
  "description": "Torrent proxy client that exposes webseed-like HTTP stream endpoint.",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "publishConfig": {
@@ -4831,6 +4831,26 @@ export class HlsSessionManager {
4831
4831
  *
4832
4832
  * @param {HlsSession} session
4833
4833
  */
4834
+ /**
4835
+ * How many encoders are running and not suspended right now.
4836
+ *
4837
+ * A cost measured while two of them share the machine belongs to neither.
4838
+ *
4839
+ * @returns {number}
4840
+ */
4841
+ #runningEncoders() {
4842
+ let running = 0;
4843
+ for (const session of this.sessionsById.values()) {
4844
+ if (session?.ffmpeg != null &&
4845
+ !hasChildExited(session.ffmpeg) &&
4846
+ session.encoderPaused !== true &&
4847
+ session.state !== "disposed") {
4848
+ running += 1;
4849
+ }
4850
+ }
4851
+ return running;
4852
+ }
4853
+
4834
4854
  async #learnFromEncoder(session) {
4835
4855
  if (
4836
4856
  !session ||
@@ -4866,6 +4886,16 @@ export class HlsSessionManager {
4866
4886
  if (speed === null) {
4867
4887
  return;
4868
4888
  }
4889
+ // Only while this encoder had the machine to itself. A reading taken beside
4890
+ // another encoder already contains that other work — and the budget then
4891
+ // ADDS the same work again when it predicts, so the cost is counted twice
4892
+ // and grows with every reading. Measured 2026-08-15 in the field: copying,
4893
+ // whose truth is 7.9x, was learned as 2.03x, and decoding, whose clips say
4894
+ // 2.6x, as 0.87x. Every rung was then refused, the offer collapsed to the
4895
+ // one copied height, and the viewer lost the quality menu altogether.
4896
+ if (this.#runningEncoders() > 1) {
4897
+ return;
4898
+ }
4869
4899
  if (speed < BUDGET_SPEED_OK && await this.#classifyTranscodeBound(session) === "download") {
4870
4900
  return; // the torrent is what is short; this says nothing about the host
4871
4901
  }