@torrent-tv/proxy 2.107.17 → 2.107.19

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.107.17",
3
+ "version": "2.107.19",
4
4
  "description": "Torrent proxy client that exposes webseed-like HTTP stream endpoint.",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "repository": {
@@ -301,7 +301,7 @@ export function predictPlaybackReadiness(input = {}) {
301
301
  return result(false, null, buffered, reserve, null, "link-rate-unavailable", 0);
302
302
  }
303
303
 
304
- const preparedSegments = trackState.reduce((sum, { ready, segments, tail }) =>
304
+ let preparedSegments = trackState.reduce((sum, { ready, segments, tail }) =>
305
305
  sum + segments.filter((segment) => ready.has(segment.index)).length +
306
306
  tail.filter((segment) => ready.has(segment.index)).length, 0);
307
307
  const jobs = [];
@@ -309,6 +309,7 @@ export function predictPlaybackReadiness(input = {}) {
309
309
  for (let trackIndex = 0; trackIndex < trackState.length; trackIndex += 1) {
310
310
  const state = trackState[trackIndex];
311
311
  const { track, segments } = state;
312
+ const safeThrough = position + finiteNonNegative(input.bufferedAheadSeconds) + reserve;
312
313
  const processingCoverage = numericCoverage((Array.isArray(track.processingRanges) ? track.processingRanges : [])
313
314
  .filter(range => Number.isFinite(range?.start) && Number.isFinite(range?.end) && range.end > range.start));
314
315
  const measuredBitsPerMediaSecond = state.bitsPerMediaSecond;
@@ -344,6 +345,7 @@ export function predictPlaybackReadiness(input = {}) {
344
345
  const encodeWork = produced ? 0 : remainingProcessing.reduce((sum, part) => sum + part.end - part.start, 0);
345
346
 
346
347
  let sourceByteIntervals = NO_SOURCE_BYTES;
348
+ let deferredAtBoundary = false;
347
349
  if (!produced && encodeWork > 0) {
348
350
  // Each source the piece reads, in order and once: the track's own, then
349
351
  // those its inputs name. Gathered with loops and no copies, because
@@ -362,6 +364,15 @@ export function predictPlaybackReadiness(input = {}) {
362
364
  } else if (source.source.complete !== true) {
363
365
  const ranges = exactInputs ? inputRangesOf(exactInputs, sourceId) : null;
364
366
  if (ranges === null) {
367
+ if (segment.startSeconds + state.clockSeconds > safeThrough) {
368
+ // Its source ranges lie beyond the browser's held buffer and
369
+ // reserve. The playback map will request them as the viewer
370
+ // advances; continuing to demand them here prevents startup.
371
+ state.segments = state.segments.slice(0, state.segments.indexOf(segment));
372
+ state.tail = [];
373
+ deferredAtBoundary = true;
374
+ break;
375
+ }
365
376
  if (unknownReason === null) {
366
377
  unknownReason = "source-input-ranges-unavailable";
367
378
  unavailableSource = { sourceId, trackId: track.id, segmentIndex: segment.index, phase: "near" };
@@ -373,6 +384,7 @@ export function predictPlaybackReadiness(input = {}) {
373
384
  }
374
385
  }
375
386
  }
387
+ if (deferredAtBoundary) break;
376
388
  if (!produced && encodeWork > 0 && !state.curve) {
377
389
  unknownReason ??= "encode-rate-unavailable";
378
390
  }
@@ -389,6 +401,10 @@ export function predictPlaybackReadiness(input = {}) {
389
401
  }
390
402
  }
391
403
 
404
+ preparedSegments = trackState.reduce((sum, { ready, segments, tail }) =>
405
+ sum + segments.filter((segment) => ready.has(segment.index)).length +
406
+ tail.filter((segment) => ready.has(segment.index)).length, 0);
407
+
392
408
  if (unknownReason) {
393
409
  return { ...result(false, null, buffered, reserve, null, unknownReason, preparedSegments),
394
410
  ...(unavailableSource ? { unavailableSource } : {}) };
@@ -749,8 +765,13 @@ function scheduleTail({ trackState, sourceState, sourceByteCoverage, sourceArriv
749
765
  if (!source) return { reason: "source-measurement-unavailable" };
750
766
  if (source.source.complete === true) continue;
751
767
  const ranges = exactInputs ? inputRangesOf(exactInputs, sourceId) : null;
752
- if (ranges === null) return { reason: "source-input-ranges-unavailable",
753
- unavailableSource: { sourceId, trackId: track.id, segmentIndex: segment.index, phase: "tail" } };
768
+ if (ranges === null) {
769
+ // The tail extends beyond the ranges the playback map has asked to
770
+ // resolve. Stop this projection at that boundary: the near window
771
+ // remains exact, and later segments enter the map as the viewer
772
+ // advances. Waiting for the whole tail here prevents a cold start.
773
+ return null;
774
+ }
754
775
  const bytes = supplyOf(sourceId);
755
776
  let order = 0;
756
777
  for (const range of ranges) {