@torrent-tv/proxy 2.105.7 → 2.105.9

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.105.7",
3
+ "version": "2.105.9",
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
@@ -22,7 +22,7 @@ import { probePackets } from "./services/media/probe-packets.js";
22
22
  import { trackChoicesOf } from "./services/viewer/segment-demands.js";
23
23
  import { PlaybackProjection } from "./services/torrent/PlaybackProjection.js";
24
24
  import { SegmentInputTable } from "./services/viewer/segment-input-table.js";
25
- import { SourcePreparation } from "./services/viewer/SourcePreparation.js";
25
+ import { SourcePreparation, preparationInterval } from "./services/viewer/SourcePreparation.js";
26
26
  import { handleHealthzGet } from "./routes/healthz/get.js";
27
27
  import { handleApiDeliverySinkGet } from "./routes/api/delivery-sink/get.js";
28
28
  import { handleApiLinkProbeGet } from "./routes/api/link-probe/get.js";
@@ -1403,7 +1403,7 @@ export async function startProxyServer({
1403
1403
  (work.ownerId === undefined || viewer.id === work.ownerId))
1404
1404
  .map(viewer => viewer.positionSeconds() ?? 0);
1405
1405
  const from = (positions.length ? Math.min(...positions) : 0) + (Number(info.value?.startTimeSeconds) || 0);
1406
- params.packetInterval = { from, to: from + outputParts.segmentDurationSec };
1406
+ params.packetInterval = preparationInterval(work.role, from, outputParts.segmentDurationSec);
1407
1407
  if (work.role === "subtitle-embedded") {
1408
1408
  const tracks = await containerOrchestrator.containerFor(params).then(container => container.readTracks());
1409
1409
  const track = tracks.find(track => track.type === "subtitle" && track.declaredIndex === work.trackIndex);
@@ -291,8 +291,9 @@ export class EncodeRuns {
291
291
  const endedAt = performance.now();
292
292
  run.firstWaitTraceInputAt = endedAt;
293
293
  const { sourceKey, process } = run.waitTraceInput ?? {};
294
+ const startedAt = run.waitTraceInput?.startedAt;
294
295
  this.#host.observeWait?.(sourceKey, "process", { process, phase: "end", runToken, fileIndex, bytes,
295
- startedAt: run.startedAt, endedAt, durationMs: endedAt - run.startedAt });
296
+ startedAt, endedAt, durationMs: Number.isFinite(startedAt) ? endedAt - startedAt : null });
296
297
  const f2Process = process === "F1.a" ? "F2.a" : "F2.v";
297
298
  run.waitTraceInput.f2Process = f2Process;
298
299
  run.waitTraceInput.f2StartedAt = endedAt;
@@ -1290,9 +1291,10 @@ export class EncodeRuns {
1290
1291
  }
1291
1292
  });
1292
1293
  const f1Process = session.spec.carries === "audio-only" ? "F1.a" : "F1.v";
1293
- run.waitTraceInput = { sourceKey: session.file.sourceKey, process: f1Process };
1294
+ const f1StartedAt = performance.now();
1295
+ run.waitTraceInput = { sourceKey: session.file.sourceKey, process: f1Process, startedAt: f1StartedAt };
1294
1296
  this.#host.observeWait?.(session.file.sourceKey, "process", { process: f1Process,
1295
- phase: "begin", runToken: inputToken, startedAt: run.startedAt, output: session.outputKey,
1297
+ phase: "begin", runToken: inputToken, startedAt: f1StartedAt, output: session.outputKey,
1296
1298
  from: run.from, to: run.to });
1297
1299
  if (admittedInput.original) {
1298
1300
  run.originalInput = admittedInput;
@@ -1,3 +1,31 @@
1
+ /**
2
+ * How many segments ahead of the viewer a chosen embedded subtitle is read.
3
+ *
4
+ * A reading window is taken again only when the viewer is planned again, which
5
+ * is when their report says they entered the next segment, and it starts where
6
+ * that report put them. Reports come every 1.5 s, so a window one segment long
7
+ * ended before the next one began whenever that report came later in its
8
+ * segment than the one before: a cue between the two was never read, and a
9
+ * cue just past a window was read only once the viewer had reached it (field
10
+ * 2026-10-22, Drifters 07: 1 of 27 cues missing, 7 late by 0.2-1.6 s,
11
+ * torrent-tv/meta#7). Two segments let each window reach past the start of the
12
+ * next while the viewer is planned at least once a segment.
13
+ */
14
+ const SUBTITLE_SEGMENTS_AHEAD = 2;
15
+
16
+ /**
17
+ * The stretch of film one preparation read covers from where its viewers stand.
18
+ *
19
+ * @param {string | undefined} role
20
+ * @param {number} from - Seconds on the file's own clock.
21
+ * @param {number} segmentDurationSec
22
+ * @returns {{ from: number, to: number }}
23
+ */
24
+ export function preparationInterval(role, from, segmentDurationSec) {
25
+ const segments = role === "subtitle-embedded" ? SUBTITLE_SEGMENTS_AHEAD : 1;
26
+ return { from, to: from + segments * segmentDurationSec };
27
+ }
28
+
1
29
  /** Source preparation is derived from viewers before any output exists. */
2
30
  export class SourcePreparation {
3
31
  #viewers;