@torrent-tv/proxy 2.101.38 → 2.101.40

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.101.38",
3
+ "version": "2.101.40",
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
@@ -708,8 +708,13 @@ export async function startProxyServer({
708
708
  const waiting = new Set();
709
709
  const turn = takeTurns();
710
710
  // ONLY THE SEGMENTS WHOSE RANGES ARE NOT KNOWN are worked out, in the
711
- // order the swarm is to fetch them.
711
+ // order the swarm is to fetch them. Range resolution can use either a
712
+ // direct source index or the fallback reads below; yield between zones
713
+ // in both cases so a large map cannot hold the main thread across its
714
+ // full projection.
712
715
  for (const zone of projection.unresolved()) {
716
+ if (!map.isCurrent()) return [];
717
+ await turn();
713
718
  if (!map.isCurrent()) return [];
714
719
  const reader = readers.get(zone.outputKey);
715
720
  if (!reader) continue;
@@ -764,14 +769,7 @@ export async function startProxyServer({
764
769
  waiting.add(zone.outputKey);
765
770
  continue;
766
771
  }
767
- // The pass shares the thread in equal turns (`takeTurns`): in one
768
- // stretch the first pass over a film's 4000 zones held the main thread
769
- // for all of its work, and handed back after every zone it took
770
- // 14.7-22 s while the start forecast waited for it. What was worked
771
- // out is kept, so a pass a newer map ends is not lost
772
- // (torrent-tv/meta#178).
773
772
  if (value?.kind === "result") segmentSourceRanges.set(rangesKey, value.ranges);
774
- if (await turn() && !map.isCurrent()) return [];
775
773
  if (value?.kind === "result") {
776
774
  convert(value.ranges);
777
775
  continue;
@@ -999,8 +999,13 @@ export class ViewerRequests {
999
999
  } });
1000
1000
  if (waitEstimate) forecast.waitEstimate = waitEstimate;
1001
1001
  }
1002
- if (measurement.forecastReason !== forecast.reason) {
1002
+ const unavailableInput = forecast.unavailableSource;
1003
+ const forecastInputKey = unavailableInput ? JSON.stringify([
1004
+ unavailableInput.sourceId, unavailableInput.trackId, unavailableInput.segmentIndex, unavailableInput.phase
1005
+ ]) : null;
1006
+ if (measurement.forecastReason !== forecast.reason || measurement.forecastInputKey !== forecastInputKey) {
1003
1007
  measurement.forecastReason = forecast.reason;
1008
+ measurement.forecastInputKey = forecastInputKey;
1004
1009
  logger.info(`playback readiness ${session.id} consumer=${consumerId} ${JSON.stringify({
1005
1010
  ...forecast,
1006
1011
  positionSeconds,
@@ -258,6 +258,7 @@ export function predictPlaybackReadiness(input = {}) {
258
258
  }
259
259
  const link = rateCurve(input.linkReadings, now);
260
260
  let unknownReason = null;
261
+ let unavailableSource = null;
261
262
  if (!link) {
262
263
  return result(false, null, buffered, reserve, null, "link-rate-unavailable", 0);
263
264
  }
@@ -323,7 +324,10 @@ export function predictPlaybackReadiness(input = {}) {
323
324
  } else if (source.source.complete !== true) {
324
325
  const ranges = exactInputs ? inputRangesOf(exactInputs, sourceId) : null;
325
326
  if (ranges === null) {
326
- unknownReason ??= "source-input-ranges-unavailable";
327
+ if (unknownReason === null) {
328
+ unknownReason = "source-input-ranges-unavailable";
329
+ unavailableSource = { sourceId, trackId: track.id, segmentIndex: segment.index, phase: "near" };
330
+ }
327
331
  } else {
328
332
  if (sourceByteIntervals === NO_SOURCE_BYTES) sourceByteIntervals = new Map();
329
333
  sourceByteIntervals.set(sourceId, ranges);
@@ -348,7 +352,8 @@ export function predictPlaybackReadiness(input = {}) {
348
352
  }
349
353
 
350
354
  if (unknownReason) {
351
- return result(false, null, buffered, reserve, null, unknownReason, preparedSegments);
355
+ return { ...result(false, null, buffered, reserve, null, unknownReason, preparedSegments),
356
+ ...(unavailableSource ? { unavailableSource } : {}) };
352
357
  }
353
358
  for (const { track, curve } of trackState) {
354
359
  if (jobs.some((job) => job.track === track && !job.produced) && !curve) {
@@ -671,7 +676,8 @@ function scheduleTail({ trackState, sourceState, sourceByteCoverage, sourceArriv
671
676
  if (!source) return { reason: "source-measurement-unavailable" };
672
677
  if (source.source.complete === true) continue;
673
678
  const ranges = exactInputs ? inputRangesOf(exactInputs, sourceId) : null;
674
- if (ranges === null) return { reason: "source-input-ranges-unavailable" };
679
+ if (ranges === null) return { reason: "source-input-ranges-unavailable",
680
+ unavailableSource: { sourceId, trackId: track.id, segmentIndex: segment.index, phase: "tail" } };
675
681
  const bytes = supplyOf(sourceId);
676
682
  let order = 0;
677
683
  for (const range of ranges) {