@torrent-tv/proxy 2.92.0 → 2.92.2

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.92.0",
3
+ "version": "2.92.2",
4
4
  "description": "Torrent proxy client that exposes webseed-like HTTP stream endpoint.",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "repository": {
package/server.js CHANGED
@@ -409,13 +409,13 @@ export async function startProxyServer({
409
409
  resolvePlayback: async (map) => {
410
410
  if (map.zones.length === 0) return [];
411
411
  const params = await containerOver(map);
412
- if (!params) return [];
412
+ if (!params || !map.isCurrent()) return [];
413
413
  const tracks = await containerOrchestrator.inspect(params, "tracks");
414
- if (tracks.kind !== "result") return [];
414
+ if (tracks.kind !== "result" || !map.isCurrent()) return [];
415
415
  const container = containerOrchestrator.known(map.sourceKey, map.fileIndex);
416
416
  if (typeof container?.readPacketIndex !== "function") return [];
417
417
  const media = await containerOrchestrator.inspect(params, "media-info");
418
- if (media.kind !== "result") return [];
418
+ if (media.kind !== "result" || !map.isCurrent()) return [];
419
419
  const shift = Number(media.value?.startTimeSeconds) || 0;
420
420
  const converted = [];
421
421
  const demands = [];
@@ -445,6 +445,7 @@ export async function startProxyServer({
445
445
  }
446
446
  demands.sort((left, right) => left.deadlineAt - right.deadlineAt || right.priority - left.priority || left.index - right.index);
447
447
  for (const zone of demands) {
448
+ if (!map.isCurrent()) return [];
448
449
  const selected = zone.tracks.map(choice => ({ choice, track: tracks.value.filter(track => track.type === choice.type)[choice.index] }));
449
450
  if (selected.some(({ track }) => !track)) continue;
450
451
  const wanted = selected.map(({ track }) => track);
@@ -454,8 +455,10 @@ export async function startProxyServer({
454
455
  const intervalParams = await containerOver({ ...map, packetInterval: interval,
455
456
  requestId: `download:${map.sourceKey}:${map.fileIndex}:${zone.outputKey}:${zone.index}`,
456
457
  demand: { ...zone.owner, leadSeconds: zone.leadSeconds } });
458
+ if (!map.isCurrent()) return [];
457
459
  if (!intervalParams) continue;
458
460
  const packets = await containerOrchestrator.inspect(intervalParams, "packets");
461
+ if (!map.isCurrent()) return [];
459
462
  if (packets.kind !== "result") continue;
460
463
  const input = new SegmentInputs({ index: packets.value, tracks: wanted }).forInterval({ ...interval, mode: track => modes.get(track) });
461
464
  if (input.kind !== "result") continue;
@@ -578,6 +578,15 @@ export class EncodeRun {
578
578
  this.#publishClosed(this.#pendingClosed, name);
579
579
  }
580
580
  if (this.#publicationError) break;
581
+ const index = this.indexOfName(name);
582
+ const endOfWork = Number.isFinite(endOfRun(this)) ? this.to : this.lastSegmentIndex();
583
+ // A packet crossing the final cut can flush a following file. It may
584
+ // complete its predecessor, but no unadmitted interval can be published.
585
+ if (Number.isInteger(index) && Number.isInteger(endOfWork) && index > endOfWork) {
586
+ this.#closedTimings.delete(name);
587
+ this.#pendingClosed = null;
588
+ continue;
589
+ }
581
590
  this.#pendingClosed = name;
582
591
  }
583
592
  }
@@ -1,10 +1,15 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import { createRequire } from "node:module";
3
+ import { accessSync, constants } from "node:fs";
3
4
  import { ffprobeRecord } from "./container/ffprobe-record.js";
4
5
 
5
6
  const require = createRequire(import.meta.url);
6
7
  function bundledProbe() {
7
- try { return require("@ffprobe-installer/ffprobe").path; }
8
+ try {
9
+ const binary = require("@ffprobe-installer/ffprobe").path;
10
+ accessSync(binary, constants.X_OK);
11
+ return binary;
12
+ }
8
13
  catch { return "ffprobe"; }
9
14
  }
10
15
 
@@ -44,7 +44,8 @@ export class DownloadMaps {
44
44
  }
45
45
  if (changed) await this.#emit(file);
46
46
  if (revision !== file.revision) return;
47
- const resolved = await this.#resolvePlayback({ sourceKey, fileIndex, durationSeconds, zones: file.sourceZones });
47
+ const resolved = await this.#resolvePlayback({ sourceKey, fileIndex, durationSeconds, zones: file.sourceZones,
48
+ isCurrent: () => this.#files.get(`${sourceKey}:${fileIndex}`) === file && revision === file.revision });
48
49
  if (revision !== file.revision) return;
49
50
  file.zones = resolved;
50
51
  return this.#emit(file);
@@ -53,7 +54,18 @@ export class DownloadMaps {
53
54
  refresh(sourceKey, fileIndex) {
54
55
  const file = this.#files.get(`${sourceKey}:${fileIndex}`);
55
56
  if (!file) return Promise.resolve();
56
- return this.playback({ sourceKey, fileIndex, durationSeconds: file.durationSeconds, zones: file.sourceZones });
57
+ file.refreshAgain = true;
58
+ if (file.refreshPending) return file.refreshPending;
59
+ file.refreshPending = (async () => {
60
+ try {
61
+ do {
62
+ file.refreshAgain = false;
63
+ if (this.#files.get(`${sourceKey}:${fileIndex}`) !== file) return;
64
+ await this.playback({ sourceKey, fileIndex, durationSeconds: file.durationSeconds, zones: file.sourceZones });
65
+ } while (file.refreshAgain);
66
+ } finally { file.refreshPending = null; }
67
+ })();
68
+ return file.refreshPending;
57
69
  }
58
70
 
59
71
  epoch(sourceKey, fileIndex) {
@@ -95,7 +107,9 @@ export class DownloadMaps {
95
107
  deadlineAt = Number.isFinite(current.deadlineAt) ? current.deadlineAt - leadSeconds * 1000 : current.deadlineAt;
96
108
  }
97
109
  file.metadata.set(statement, { ranges: result.ranges, requestId: result.requestId, priority, urgent, deadlineAt, interval, leadSeconds, scope, order });
98
- } else {
110
+ } else if (result.kind !== "needs-memory") {
111
+ // Allocation refusal says nothing about whether previously missing
112
+ // source bytes have arrived. Keep their demand until the read resolves.
99
113
  file.metadata.delete(statement);
100
114
  }
101
115
  return this.#emit(file);