@torrent-tv/proxy 2.100.5 → 2.100.7

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.100.5",
3
+ "version": "2.100.7",
4
4
  "description": "Torrent proxy client that exposes webseed-like HTTP stream endpoint.",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "repository": {
@@ -32,18 +32,28 @@ import { logger } from "../../utils/logger.js";
32
32
  export const CONTAINER_HEAD_BYTES = 256 * 1024;
33
33
 
34
34
  /**
35
- * The size of the file and its OpenSubtitles hash, from the two edges. A read
36
- * that has not arrived throws `BytesUnavailable`, so nothing partial is kept.
35
+ * The size of the file and its OpenSubtitles hash, from the two edges. Nothing
36
+ * partial is kept: an edge that has not arrived is named instead.
37
+ *
38
+ * Both edges are read at once and both missing ones named in one answer. Read
39
+ * one after the other, a cold start asked the swarm for the file's last piece
40
+ * only once its first had arrived: on Home Assistant the last piece, which
41
+ * also holds a Matroska file's Cues, came 2.4 s after the first and the start
42
+ * waited for it (r66, torrent-tv/meta#178).
37
43
  *
38
44
  * @param {{ readRange: (start: number, end: number) => Promise<Buffer | null>, fileSize: number }} params
39
- * @returns {Promise<{ hash: string, size: number } | null>} `null` for a file too short to have one.
45
+ * @returns {Promise<{ hash: string, size: number } | { missing: Array<[number, number]> } | null>}
46
+ * `null` for a file too short to have one.
40
47
  */
41
48
  async function readFingerprint({ readRange, fileSize }) {
42
49
  if (!hasOshash(fileSize)) return null;
43
50
  const read = strictReader(readRange, fileSize);
44
- const head = await read(0, OSHASH_EDGE_BYTES - 1);
45
- const tail = await read(fileSize - OSHASH_EDGE_BYTES, fileSize - 1);
46
- return { hash: oshash(fileSize, head, tail), size: fileSize };
51
+ const reads = await Promise.allSettled([read(0, OSHASH_EDGE_BYTES - 1), read(fileSize - OSHASH_EDGE_BYTES, fileSize - 1)]);
52
+ const failed = reads.find(reading => reading.status === "rejected" && !isUnavailable(reading.reason));
53
+ if (failed) throw failed.reason;
54
+ const missing = reads.filter(reading => reading.status === "rejected").map(reading => [reading.reason.start, reading.reason.end]);
55
+ if (missing.length) return { missing };
56
+ return { hash: oshash(fileSize, reads[0].value, reads[1].value), size: fileSize };
47
57
  }
48
58
 
49
59
  /**
@@ -181,6 +191,10 @@ export class ContainerOrchestrator {
181
191
  if (statement === "fingerprint") {
182
192
  // A fact about the bytes at the two ends of the file, whatever the format.
183
193
  const value = await readFingerprint(params);
194
+ if (value?.missing) {
195
+ logger.info(`media request=${requestId} needs bytes=${value.missing.map(([start, end]) => `${start}-${end}`).join(",")}`);
196
+ return { kind: "needs-ranges", ranges: value.missing, requestId };
197
+ }
184
198
  return value ? { kind: "result", value, requestId } : { kind: "terminal", reason: "file-too-short", requestId };
185
199
  }
186
200
  const container = await this.containerFor(params);
@@ -855,7 +855,12 @@ export class ViewerRequests {
855
855
  const segments = [];
856
856
  for (let index = 0; index < count; index++) {
857
857
  const endSeconds = timeline.publishedStartOf(index + 1);
858
- const ranges = this.#host.encodeRuns.finishedMediaRangesOf(output, index);
858
+ // Only a made piece has media intervals to read. Asked of every piece,
859
+ // each one not made cost a `stat` of its file in the store's safety net
860
+ // for a missed directory event: 1327 per poll per output, 12.6 s of 300 s
861
+ // of the main thread at a cold start on Home Assistant (r61,
862
+ // torrent-tv/meta#178), while requests waited behind it.
863
+ const ranges = inventory.has(index) ? this.#host.encodeRuns.finishedMediaRangesOf(output, index) : undefined;
859
864
  const mediaRanges = ranges && output.segmentFormat?.servedMediaRanges ?
860
865
  output.segmentFormat.servedMediaRanges(ranges, { initBytes: sessionInit }) : undefined;
861
866
  if (index !== 0 && mediaRanges === undefined && !inventory.has(index) &&