@torrent-tv/proxy 2.97.13 → 2.97.14

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.97.13",
3
+ "version": "2.97.14",
4
4
  "description": "Torrent proxy client that exposes webseed-like HTTP stream endpoint.",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "repository": {
@@ -461,6 +461,17 @@ export class EncodeInputs {
461
461
  if (this.#requests.get(request.key) !== request) return undefined;
462
462
  }
463
463
  const chosen = stretches.get(end);
464
+ // EVERY BYTE IS HERE BEFORE THE RUN IS ANSWERED. The stretch is copied
465
+ // after the answer (`OriginalInput.js`), so a stretch that is not all
466
+ // downloaded would start an encoder whose input then fails; checked here,
467
+ // it waits for the bytes as one read of everything used to. Field
468
+ // 2026-10-12: a cold MKV's first piece was answered, its stretch read
469
+ // found pieces missing, and the run was refused for good (torrent-tv/meta#178).
470
+ if (this.#heldRanges !== null && !await this.#present(chosen.sources, held, request.timing)) {
471
+ const [first] = chosen.sources;
472
+ return { kind: "needs-bytes", sourceKey: first.sourceKey, fileIndex: first.fileIndex, ranges: first.input.ranges };
473
+ }
474
+ if (this.#requests.get(request.key) !== request) return undefined;
464
475
  this.#wanted.set(request.key, { wanted: chosen.bytes, required: chosen.bytes });
465
476
  result = await admitOriginalInput({ sources: chosen.sources, readRanges: this.#read, now: this.#now,
466
477
  reserve: async bytes => this.#hold(request, bytes) });
@@ -1088,7 +1088,10 @@ export class EncodeRuns {
1088
1088
  // torrent cannot tell them apart.
1089
1089
  lastSegmentIndex: () =>
1090
1090
  run.to,
1091
- inputUnavailable: (message) => isInputUnavailable(message),
1091
+ // A stretch whose copy failed after the run started — a piece left the
1092
+ // store between the check and the copy — is an input that went away, not
1093
+ // a fault of the input: the plan asks for it again (torrent-tv/meta#178).
1094
+ inputUnavailable: (message) => isInputUnavailable(message) || admittedInput?.copyFailed?.() === true,
1092
1095
  onProgress: (report) => this.#noteRunProgress(session, run, report),
1093
1096
  // A closed piece moves this encoder forward, perhaps out of the band that
1094
1097
  // led; who has the processor is decided again at once.
@@ -1370,7 +1373,8 @@ export class EncodeRuns {
1370
1373
  // been dead code — which is also why nothing ever stopped the restart loop
1371
1374
  // recorded in the field the same day.
1372
1375
  const wasCurrent = this.runsOf(session).includes(run);
1373
- const inputFailed = run.admittedInputKeys && failedAdmittedInput(ended,
1376
+ // A stretch that could not be copied says nothing of the input itself.
1377
+ const inputFailed = run.admittedInputKeys && run.originalInput?.copyFailed?.() !== true && failedAdmittedInput(ended,
1374
1378
  session.spec.transcodesVideo && this.#host.videoEncoder.kind !== "software");
1375
1379
  if (inputFailed) {
1376
1380
  const index = run.head;