@torrent-tv/proxy 2.80.6 → 2.80.8
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.
|
@@ -324,6 +324,21 @@ export class EncodeOrchestrator {
|
|
|
324
324
|
)
|
|
325
325
|
});
|
|
326
326
|
|
|
327
|
+
// A move is the plan taking a running encoder away from where it already
|
|
328
|
+
// stands, which is exactly the decision that was found wandering back and
|
|
329
|
+
// forth in the field on 2026-09-07 with no way to see why: the "because"
|
|
330
|
+
// line names the comparison in words, never the numbers it was decided
|
|
331
|
+
// from. Said here, once per reconcile, and only when a move actually
|
|
332
|
+
// happens — everything a rerun of the same decision needs: the windows
|
|
333
|
+
// this call saw (priority, the real time, which side of the viewers),
|
|
334
|
+
// the budget, and where every live run stood.
|
|
335
|
+
if (actions.some((action) => action.type === "move")) {
|
|
336
|
+
this.logger.info(
|
|
337
|
+
`encode-plan move on ${address}: windows=${JSON.stringify(windows)} ` +
|
|
338
|
+
`maxRuns=${this.#affordableOn(address, live)} ` +
|
|
339
|
+
`live=${JSON.stringify(live.map((run) => ({ from: run.from, to: run.to, head: run.head, speedX: run.speedX })))}`
|
|
340
|
+
);
|
|
341
|
+
}
|
|
327
342
|
for (const action of actions) {
|
|
328
343
|
if (action.type === "stop") {
|
|
329
344
|
this.#stop(action.run, action.because);
|
|
@@ -575,22 +575,26 @@ export class SharedPieceStore {
|
|
|
575
575
|
// the machine was full kept a small allowance for its whole life, however
|
|
576
576
|
// much memory was freed afterwards (roadmap item 2, 2026-09-02).
|
|
577
577
|
const wanted = Math.floor(Number(allowedBytes) / this.#chunkLength);
|
|
578
|
-
// Never below
|
|
579
|
-
//
|
|
580
|
-
//
|
|
581
|
-
//
|
|
582
|
-
//
|
|
583
|
-
// 2026-08-15
|
|
584
|
-
//
|
|
578
|
+
// Never below what the live readers together hold, even when the machine's
|
|
579
|
+
// share says less. A store that cannot hold what its readers are pinning
|
|
580
|
+
// cannot complete any of their reads at all: every resident piece ends up
|
|
581
|
+
// pinned, a read returns zero bytes and ffmpeg takes that for the end of
|
|
582
|
+
// the file, which killed every encoder on that file in the field on
|
|
583
|
+
// 2026-08-15 (one reader) and again on 2026-09-07 (three readers of one
|
|
584
|
+
// file at once — picture, sound and the edge-warming read — where this
|
|
585
|
+
// floor was still computed from only the widest one of them, `wantedBytes`
|
|
586
|
+
// above already asks for the union and got it right; this is the same
|
|
587
|
+
// union, used as the floor instead of only as the ask). Exceeding the share
|
|
588
|
+
// is the lesser failure, and the line below says when it happens.
|
|
585
589
|
const demand = this.#lru.demand();
|
|
586
590
|
this.#growthCeiling = Math.max(
|
|
587
591
|
MIN_RESIDENT_PIECES,
|
|
588
|
-
demand.readers > 0 ? demand.
|
|
592
|
+
demand.readers > 0 ? demand.unionPieces : MIN_RESIDENT_PIECES,
|
|
589
593
|
Number.isFinite(wanted) ? wanted : MIN_RESIDENT_PIECES
|
|
590
594
|
);
|
|
591
595
|
const belowAWindow = demand.readers > 0
|
|
592
596
|
&& Number.isFinite(wanted)
|
|
593
|
-
&& wanted < demand.
|
|
597
|
+
&& wanted < demand.unionPieces;
|
|
594
598
|
this.#beyondTheMachine = belowAWindow;
|
|
595
599
|
// The LRU is told too. It was constructed with the store's original
|
|
596
600
|
// capacity and never revised, so `isFull()` answered against a number that
|
|
@@ -278,6 +278,32 @@ test("the store asks for what its readers declared, and for a whole window at le
|
|
|
278
278
|
}
|
|
279
279
|
});
|
|
280
280
|
|
|
281
|
+
test("the floor the ceiling will not fall below is the same union, not the widest reader alone", async () => {
|
|
282
|
+
const { store, directory } = await makeStore(64);
|
|
283
|
+
try {
|
|
284
|
+
// The same two overlapping readers as the ask above: picture and sound,
|
|
285
|
+
// 10..44, thirty-five pieces together. The machine now offers far less
|
|
286
|
+
// than that (ten pieces) — before this fix the floor here came from
|
|
287
|
+
// `widestPieces` (twenty, the wider of the two readers alone), fifteen
|
|
288
|
+
// short of what both of them were actually pinning at once. A field
|
|
289
|
+
// session with three such readers on one file (video, audio and the
|
|
290
|
+
// edge-warming read) reached exactly that shortfall on 2026-09-07: every
|
|
291
|
+
// resident piece ended up pinned and WebTorrent destroyed the torrent
|
|
292
|
+
// over it.
|
|
293
|
+
store.protectRange("video", 10, 29);
|
|
294
|
+
store.protectRange("audio", 25, 44);
|
|
295
|
+
const revised = store.reviseGrowthCeiling(10 * PIECE);
|
|
296
|
+
assert.equal(
|
|
297
|
+
revised.ceilingBytes,
|
|
298
|
+
35 * PIECE,
|
|
299
|
+
"the floor is the union of both readers, not the wider one alone"
|
|
300
|
+
);
|
|
301
|
+
} finally {
|
|
302
|
+
store.destroy(() => undefined);
|
|
303
|
+
await fs.rm(directory, { recursive: true, force: true });
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
|
|
281
307
|
test("a block is re-used instead of a new one being allocated for every piece", async () => {
|
|
282
308
|
const capacity = 4;
|
|
283
309
|
const { store, directory } = await makeStore(capacity);
|