@torrent-tv/proxy 2.99.16 → 2.99.17

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.99.16",
3
+ "version": "2.99.17",
4
4
  "description": "Torrent proxy client that exposes webseed-like HTTP stream endpoint.",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "repository": {
@@ -21,6 +21,7 @@ import { computeOutputDimensions } from "./args.js";
21
21
  import { buildAdmittedCommand } from "./admitted-command.js";
22
22
  import { buildOriginalCommand } from "./source-command.js";
23
23
  import { writeAdmittedInput } from "./AdmittedInput.js";
24
+ import { nextPacketInputs } from "./next-packet-input.js";
24
25
  import { cutOf, cutShiftOf, judgeNeighbors, judgePiece, leadingSecondsOf, lacksOnlyItsLeadingPictures } from "./piece-completeness.js";
25
26
  import { leadingPictures, presentationSegment } from "./segment-formats/presentation-segment.js";
26
27
  import { InputFailures, failedAdmittedInput } from "./InputFailures.js";
@@ -1262,23 +1263,23 @@ export class EncodeRuns {
1262
1263
  }
1263
1264
  } else if (admittedInput) {
1264
1265
  run.admittedInputKeys = new Map([[safeIndex, inputKey]]);
1265
- let nextIndex = startIndex + 1;
1266
+ const nextIndex = startIndex + 1;
1266
1267
  const canAppend = index => this.isLive(session) && run.isAlive &&
1267
1268
  this.#host.encodeOrchestrator.wantedSegmentsOn(session.outputKey).some(window => index >= window.from && index <= window.to) &&
1268
1269
  !this.liveRunsOf(session).some(other => other !== run && index >= other.from && index <= other.to) &&
1269
1270
  !this.#host.producedNumbers(session).includes(index);
1271
+ // Only an input already prepared is appended, and the one after it is
1272
+ // begun at once (`next-packet-input.js`).
1273
+ const following = nextPacketInputs({ take: index => this.#host.encodeInputs.take(session, index, index),
1274
+ canAppend, first: nextIndex, last: ordered?.to ?? startIndex });
1270
1275
  void writeAdmittedInput(admittedInput, run.process.stdin, { next: async () => {
1271
- if (nextIndex > (ordered?.to ?? startIndex) || !canAppend(nextIndex)) return null;
1272
- const next = await this.#host.encodeInputs.acquire(session, nextIndex, nextIndex);
1273
- if (!next) return null;
1274
- if (!canAppend(nextIndex)) { next.release(); return null; }
1275
- run.admittedInputKeys.set(nextIndex, this.#inputFailures.key(next, inputParameters));
1276
- run.to = nextIndex++;
1276
+ const next = following.next(index => { run.to = index; });
1277
+ if (next) run.admittedInputKeys.set(run.to, this.#inputFailures.key(next, inputParameters));
1277
1278
  return next;
1278
- }, waiting: waits => { if (waits) run.inputWaitBegins(); else run.inputWaitEnds(); } }).catch(error => {
1279
+ } }).catch(error => {
1279
1280
  this.#host.logger.warn(`encode input output=${session.outputKey} segment=${safeIndex} write failed: ${error.message}`);
1280
1281
  run.process?.stdin?.destroy();
1281
- });
1282
+ }).finally(() => following.release());
1282
1283
  }
1283
1284
  this.#runsByInputToken.set(inputToken, run);
1284
1285
  this.#host.encodersChanged?.();
@@ -0,0 +1,56 @@
1
+ /**
2
+ * @file Which input a run fed packet by packet goes on with.
3
+ *
4
+ * Such a run keeps its input open while more is appended, and FFmpeg closes a
5
+ * piece only once it reads past the piece's end, so a piece is published when
6
+ * the input after it arrives or the input ends. Waited for, the next input
7
+ * held each piece back for as long as preparing it took: on Home Assistant a
8
+ * soundtrack's #7 waited 14 s for #8's attempt, which ended refused memory, and
9
+ * the picture stood at 61.8 s (r54, r55, torrent-tv/meta#178).
10
+ *
11
+ * So a run is given the next input only when it is ready, and the one after it
12
+ * is begun as soon as one is taken: the run goes on while preparation keeps
13
+ * ahead of it, and ends, publishing its last piece, when it does not. The plan
14
+ * starts the next run on the input being prepared.
15
+ */
16
+
17
+ /**
18
+ * A source of the inputs after a run's first.
19
+ *
20
+ * @param {object} params
21
+ * @param {(index: number) => object | null} params.take - The input of one
22
+ * piece if ready, beginning its preparation otherwise (`EncodeInputs.take`).
23
+ * @param {(index: number) => boolean} params.canAppend - Whether the piece may
24
+ * still be made by this run.
25
+ * @param {number} params.first - The first piece after the run's own.
26
+ * @param {number} params.last - The last piece the run may reach.
27
+ * @returns {{ next: (onTaken?: (index: number) => void) => object | null, release: () => void }}
28
+ */
29
+ export function nextPacketInputs({ take, canAppend, first, last }) {
30
+ let index = first;
31
+ let ahead = null;
32
+ const begin = at => {
33
+ if (at <= last && canAppend(at)) ahead = take(at);
34
+ };
35
+ begin(index);
36
+ return {
37
+ next(onTaken) {
38
+ if (index > last || !canAppend(index)) return null;
39
+ const input = ahead ?? take(index);
40
+ ahead = null;
41
+ if (!input) return null;
42
+ if (!canAppend(index)) {
43
+ input.release();
44
+ return null;
45
+ }
46
+ onTaken?.(index);
47
+ index += 1;
48
+ begin(index);
49
+ return input;
50
+ },
51
+ release() {
52
+ ahead?.release();
53
+ ahead = null;
54
+ }
55
+ };
56
+ }