@torrent-tv/proxy 2.107.39 → 2.107.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.107.39",
3
+ "version": "2.107.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": {
@@ -687,8 +687,16 @@ export function predictPlaybackReadiness(input = {}) {
687
687
  }
688
688
  const tailStock = [];
689
689
  if (trackState.some(({ tailEndSeconds }) => Number.isFinite(tailEndSeconds))) {
690
- const stop = scheduleTail({ trackState, sourceState, sourceByteCoverage, runStarts, trackFinish,
691
- link, position, buffered, reserve, transferFinish, constrain, duration, stock: tailStock });
690
+ // Where each source's bytes stop being the near window's: the last byte
691
+ // its pieces read, in the file's own offsets.
692
+ const windowEnds = new Map();
693
+ for (const { sourceByteIntervals } of jobs) {
694
+ for (const [sourceId, ranges] of sourceByteIntervals) {
695
+ for (const range of ranges) windowEnds.set(sourceId, Math.max(windowEnds.get(sourceId) ?? 0, range.end));
696
+ }
697
+ }
698
+ const stop = scheduleTail({ trackState, sourceState, runStarts, trackFinish,
699
+ link, position, buffered, reserve, transferFinish, constrain, duration, stock: tailStock, windowEnds });
692
700
  if (stop) {
693
701
  return { ...result(false, null, buffered, reserve, null, stop.reason, preparedSegments),
694
702
  ...(stop.unavailableSource ? { unavailableSource: stop.unavailableSource } : {}) };
@@ -786,8 +794,8 @@ function isPiece(segment) {
786
794
  *
787
795
  * @returns {{ reason: string, unavailableSource?: object } | null} Why there is no answer, or null.
788
796
  */
789
- function scheduleTail({ trackState, sourceState, sourceByteCoverage, runStarts, trackFinish, link,
790
- position, buffered, reserve, transferFinish, constrain, duration, stock = null }) {
797
+ function scheduleTail({ trackState, sourceState, runStarts, trackFinish, link,
798
+ position, buffered, reserve, transferFinish, constrain, duration, stock = null, windowEnds = null }) {
791
799
  const tailStart = Math.max(position, ...trackState.map(state => state.tailStartSeconds));
792
800
  if (!Number.isFinite(tailStart)) return null;
793
801
  const sourceDurations = new Map();
@@ -804,10 +812,11 @@ function scheduleTail({ trackState, sourceState, sourceByteCoverage, runStarts,
804
812
  const source = sourceState.get(sourceId);
805
813
  if (!source) return { reason: "source-measurement-unavailable" };
806
814
  if (source.source.complete === true) continue;
807
- // Held means on this proxy now, past the bytes the near window already
808
- // scheduled: those are counted above, and the window's planned arrivals
809
- // counted again as held took 117 s of film off a 90-minute tail.
810
- const windowEnd = Math.max(0, ...(sourceByteCoverage.get(sourceId) ?? []).map(range => range.end));
815
+ // Held means on this proxy now, past the last byte the near window's
816
+ // pieces read: those are counted above, and the window's planned arrivals
817
+ // counted again as held took 117 s of film off a 90-minute tail. The
818
+ // torrent thread's plan is not that end: it can reach the end of the file.
819
+ const windowEnd = windowEnds?.get(sourceId) ?? 0;
811
820
  const bytes = estimateUnmappedSourceBytes(source.source, seconds, duration,
812
821
  heldBeyond(source.source.residence, windowEnd), windowEnd);
813
822
  if (bytes === null) return { reason: "source-measurement-unavailable",