@torrent-tv/proxy 2.96.9 → 2.96.10

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.96.9",
3
+ "version": "2.96.10",
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
@@ -54,6 +54,7 @@ import { wireOutputs } from "./services/server/wire-outputs.js";
54
54
  import { createPlaybackPlanner } from "./services/media/playback-planner.js";
55
55
  import { KeyframeTables } from "./services/media/KeyframeTables.js";
56
56
  import { contentsOf } from "./services/torrent/Contents.js";
57
+ import { joinedWithinPieces } from "./services/torrent/demand/pieces.js";
57
58
  import { SubtitleOrchestrator } from "./services/media/SubtitleOrchestrator.js";
58
59
  import { containerOrchestrator, CONTAINER_HEAD_BYTES, describeWorkTags } from "./services/media/ContainerOrchestrator.js";
59
60
  import { readPlaybackDeclarations } from "./services/media/read-playback-declarations.js";
@@ -434,6 +435,9 @@ export async function startProxyServer({
434
435
  const media = await containerOrchestrator.inspect(params, "media-info");
435
436
  if (media.kind !== "result" || !map.isCurrent()) return [];
436
437
  const shift = Number(media.value?.startTimeSeconds) || 0;
438
+ // A zone's ranges are stated to the torrent at the precision it fetches:
439
+ // ranges closer than a piece hold the same pieces as one.
440
+ const pieceLength = Number(torrentPool.knownTorrent(map.sourceKey)?.pieceLength);
437
441
  const converted = [];
438
442
  const demands = [];
439
443
  for (const output of outputParts.outputs.values()) {
@@ -483,7 +487,7 @@ export async function startProxyServer({
483
487
  modes: Object.fromEntries(wanted.map(track => [track.trackNumber, modes.get(track)])) };
484
488
  const { tracks: _choices, owner: _owner, sourceInterval: _sourceInterval, ...demand } = zone;
485
489
  const convert = ranges => {
486
- for (const [byteStart, byteEnd] of ranges) converted.push({ ...demand,
490
+ for (const [byteStart, byteEnd] of joinedWithinPieces(ranges, pieceLength)) converted.push({ ...demand,
487
491
  downloadInterval: { from: zone.owner.from, to: zone.owner.to }, byteStart, byteEnd });
488
492
  };
489
493
  // The bytes one segment needs are a fact of the file and the output, so
@@ -106,3 +106,29 @@ export function nearestFirst({ byteStart, byteEnd, parts }) {
106
106
  }
107
107
  return ranges;
108
108
  }
109
+
110
+ /**
111
+ * Byte ranges joined wherever the gap between two of them is shorter than a
112
+ * piece: the pieces they hold are the same, and there are fewer of them.
113
+ *
114
+ * A gap shorter than a piece cannot hold a whole piece, so every piece it
115
+ * touches is touched by a range beside it. The soundtrack of an AVI read from
116
+ * its own packets is one range per packet, interleaved with the picture's
117
+ * bytes: field 2026-10-10 (torrent-tv/meta#166), a map of 350 191 zones took
118
+ * 20.8 s to reach the torrent thread and grew its heap to 818 MB, for pieces of
119
+ * 4 MiB that the whole soundtrack touches anyway.
120
+ *
121
+ * @param {Array<[number, number]>} ranges - Inclusive byte ranges in ascending order.
122
+ * @param {number} pieceLength
123
+ * @returns {Array<[number, number]>} The same ranges when the piece length is not usable.
124
+ */
125
+ export function joinedWithinPieces(ranges, pieceLength) {
126
+ if (!Number.isFinite(pieceLength) || pieceLength <= 0) return ranges;
127
+ const joined = [];
128
+ for (const [start, end] of ranges) {
129
+ const previous = joined.at(-1);
130
+ if (previous && start >= previous[0] && start - previous[1] - 1 < pieceLength) previous[1] = Math.max(previous[1], end);
131
+ else joined.push([start, end]);
132
+ }
133
+ return joined;
134
+ }