@torrent-tv/proxy 2.76.4 → 2.76.6

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.
@@ -57,6 +57,8 @@ export const Command = {
57
57
  FILE_STATS: "file-stats",
58
58
  /** Bytes every torrent here has moved, for pricing the torrent's own cost. */
59
59
  TORRENT_TOTALS: "torrent-totals",
60
+ /** Which films this proxy holds right now, for content affinity. */
61
+ HELD_TORRENTS: "held-torrents",
60
62
  /** Reorder piece selection around a read position (seek prioritisation). */
61
63
  PRIORITIZE: "prioritize",
62
64
  /** Read a byte range; the body arrives as CHUNK messages. */
@@ -375,6 +375,29 @@ async function runCommand(command, params, id) {
375
375
  return released;
376
376
  }
377
377
 
378
+ case Command.HELD_TORRENTS: {
379
+ // Which films this proxy has right now, and how much of each. Answered
380
+ // from the live client rather than from the main thread's map of
381
+ // stand-ins, which is only cleared on shutdown and would name films this
382
+ // proxy let go of hours ago.
383
+ const held = [];
384
+ for (const torrent of pool.client?.torrents ?? []) {
385
+ const infoHash = String(torrent?.infoHash ?? "");
386
+ if (!infoHash) {
387
+ continue;
388
+ }
389
+ held.push({
390
+ infoHash,
391
+ // A viewer sent here for a film nobody has downloaded any of gains
392
+ // nothing, so the share is reported and the decision is made where
393
+ // the viewer is.
394
+ progress: Number.isFinite(torrent?.progress) ? torrent.progress : 0,
395
+ bytes: Number.isFinite(torrent?.downloaded) ? torrent.downloaded : 0
396
+ });
397
+ }
398
+ return { held };
399
+ }
400
+
378
401
  case Command.TORRENT_TOTALS: {
379
402
  // Downloaded and uploaded are counted apart: hashing every downloaded
380
403
  // byte is work of a different order from sending one back to the swarm,
@@ -250,8 +250,24 @@ export function createTunnelClient({
250
250
 
251
251
  // Health check: server requests current metrics for proxy scoring.
252
252
  if (message.type === "health-request") {
253
- const metrics = typeof onHealthRequest === "function" ? onHealthRequest() : {};
254
- send({ type: "health-response", requestId: message.requestId, metrics });
253
+ // Awaited: the answer now carries which films this proxy holds, and the
254
+ // truthful list of those lives on the torrent thread.
255
+ void (async () => {
256
+ let answer = {};
257
+ try {
258
+ answer = typeof onHealthRequest === "function" ? await onHealthRequest() : {};
259
+ } catch {
260
+ // silent-ok: a proxy that cannot describe itself is scored on
261
+ // nothing rather than not answered at all, which would drop it out
262
+ // of every selection until the next poll.
263
+ }
264
+ send({
265
+ type: "health-response",
266
+ requestId: message.requestId,
267
+ metrics: answer?.metrics ?? answer ?? {},
268
+ holds: Array.isArray(answer?.holds) ? answer.holds : []
269
+ });
270
+ })();
255
271
  return;
256
272
  }
257
273
  });
@@ -0,0 +1,94 @@
1
+ /**
2
+ * @file What encoding costs this machine, asked of the object that owns it.
3
+ *
4
+ * The arithmetic itself is exercised end to end by `auto-quality-step` and
5
+ * `quality-variants`, which go through the session manager. What is pinned here
6
+ * is the seam the move created: this object is given the host's readings as a
7
+ * QUESTION rather than a copy, and it holds what an encoder taught it.
8
+ */
9
+
10
+ import test from "node:test";
11
+ import assert from "node:assert/strict";
12
+ import { EncodeCost } from "../services/quality/EncodeCost.js";
13
+
14
+ /**
15
+ * @param {object} [readings]
16
+ * @returns {{ cost: EncodeCost, asked: () => number, host: { share: number } }}
17
+ */
18
+ function costOn(readings = {}) {
19
+ let asked = 0;
20
+ const host = { share: 1 };
21
+ const cost = new EncodeCost({
22
+ liveOutputs: { familyOf: () => [], variantHeightOf: () => 0 },
23
+ host: () => {
24
+ asked += 1;
25
+ return {
26
+ benchmark: readings.benchmark ?? null,
27
+ decodeModel: null,
28
+ contentionPenalties: null,
29
+ availability: { known: true, share: host.share }
30
+ };
31
+ },
32
+ audioCostKey: () => "audio",
33
+ runningEncoders: () => 0,
34
+ encodersRunningNow: () => 0,
35
+ torrentCostSecFor: () => 0
36
+ });
37
+ return { cost, asked: () => asked, host };
38
+ }
39
+
40
+ test("the host is asked at the moment of the question, not when this was built", () => {
41
+ // The share of the machine that is free is re-read every few seconds. Copied
42
+ // into this object when it was made, every later rung would be priced against
43
+ // a machine that has gone.
44
+ const { cost, asked, host } = costOn();
45
+ assert.equal(asked(), 0, "nothing is read until something is asked");
46
+
47
+ cost.sustainableHeights({ heights: [1080], sourceWidth: 1920, sourceHeight: 1080, fps: 24, source: null, transcodeVideo: true, ownHeight: 0 });
48
+ const first = asked();
49
+ assert.ok(first > 0);
50
+
51
+ host.share = 0.1;
52
+ cost.sustainableHeights({ heights: [1080], sourceWidth: 1920, sourceHeight: 1080, fps: 24, source: null, transcodeVideo: true, ownHeight: 0 });
53
+ assert.ok(asked() > first, "and read again on the next question");
54
+ });
55
+
56
+ test("with no benchmark to judge by, every height offered is kept", () => {
57
+ // Nothing measured is not the same as nothing possible. Refusing here would
58
+ // hide the whole ladder on a host whose startup measurement failed.
59
+ const { cost } = costOn({ benchmark: null });
60
+ const kept = cost.sustainableHeights({
61
+ heights: [1080, 720, 480],
62
+ sourceWidth: 1920,
63
+ sourceHeight: 1080,
64
+ fps: 24,
65
+ source: null,
66
+ transcodeVideo: true,
67
+ ownHeight: 0
68
+ });
69
+ assert.deepEqual(kept, [1080, 720, 480]);
70
+ });
71
+
72
+ test("a rung measured below realtime is withdrawn, and a copied source height is not", () => {
73
+ const { cost } = costOn({ benchmark: null });
74
+ const kept = cost.sustainableHeights({
75
+ heights: [1080, 480],
76
+ sourceWidth: 1920,
77
+ sourceHeight: 1080,
78
+ fps: 24,
79
+ source: null,
80
+ // The base copies its picture, so the source height costs no encoder.
81
+ transcodeVideo: false,
82
+ ownHeight: 1080,
83
+ measuredHeights: new Map([[480, 0.4]])
84
+ });
85
+ assert.deepEqual(kept, [1080], "the copy stays; the rung seen failing does not");
86
+ });
87
+
88
+ test("what an encoder taught this host is held here, and nowhere else", () => {
89
+ const { cost } = costOn();
90
+ cost.copyCost.set("torrent:abc:0", { costSec: 0.125, readings: [8], version: 1 });
91
+ assert.equal(cost.copyCost.get("torrent:abc:0").costSec, 0.125);
92
+ assert.equal(cost.audioCost.size, 0);
93
+ assert.equal(cost.decodeCost.size, 0);
94
+ });