@torrent-tv/proxy 2.97.19 → 2.97.20

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.97.19",
3
+ "version": "2.97.20",
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
@@ -79,6 +79,7 @@ import { readHostFingerprint } from "./services/encode/fingerprint.js";
79
79
  import { measureStartAndStop } from "./services/encode/start-stop-cost.js";
80
80
  import { benchmarkAudio } from "./services/encode/audio-calibration.js";
81
81
  import { logger } from "./utils/logger.js";
82
+ import { takeTurns } from "./utils/take-turns.js";
82
83
  import { completedFilesRoot } from "./services/storage/files/CompletedFiles.js";
83
84
  import { startupCpuProfile } from "./services/storage/cpu-profile.js";
84
85
  import { startProcessorReport } from "./services/storage/processor-report.js";
@@ -532,6 +533,7 @@ export async function startProxyServer({
532
533
  // answers; asking every segment held the main thread for 3.3 s on a cold
533
534
  // two-hour AVI (torrent-tv/meta#178).
534
535
  const waiting = new Set();
536
+ const turn = takeTurns();
535
537
  for (const zone of demands) {
536
538
  if (!map.isCurrent()) return [];
537
539
  // WHAT A SEGMENT READS IS WORKED OUT ONCE, not on every publication: its
@@ -571,14 +573,14 @@ export async function startProxyServer({
571
573
  value = new SegmentInputs({ index, tracks: wanted }).forInterval({ ...interval, mode: track => modes.get(track) });
572
574
  }
573
575
  } catch { value = null; }
574
- // A zone worked out gives the thread back before the next: worked out
575
- // in one stretch, the first pass over a film's 4000 zones held the main
576
- // thread 5-7 s while FFmpeg's first open waited for its answers. What
577
- // was worked out is kept, so a pass a newer map ends is not lost
576
+ // The pass shares the thread in equal turns (`takeTurns`): in one
577
+ // stretch the first pass over a film's 4000 zones held the main thread
578
+ // for all of its work, and handed back after every zone it took
579
+ // 14.7-22 s while the start forecast waited for it. What was worked
580
+ // out is kept, so a pass a newer map ends is not lost
578
581
  // (torrent-tv/meta#178).
579
582
  if (value?.kind === "result") segmentSourceRanges.set(rangesKey, value.ranges);
580
- await new Promise(resolve => setImmediate(resolve));
581
- if (!map.isCurrent()) return [];
583
+ if (await turn() && !map.isCurrent()) return [];
582
584
  if (value?.kind === "result") {
583
585
  convert(value.ranges);
584
586
  continue;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Long work on one thread, shared with the rest of that thread in equal turns.
3
+ *
4
+ * The returned function is called between steps of the work. It hands the
5
+ * thread back once the work has run, since it last did, as long as the rest
6
+ * of the thread took in its own turn then: the work takes at most about twice
7
+ * its own time, and nothing else waits longer than it took itself. Handed back
8
+ * after every step, the download map's first pass over a two-hour AVI took
9
+ * 14.7-22 s of wall time for about 4 s of its own work, and the start forecast,
10
+ * which needs the whole pass, could not answer meanwhile; held in one stretch
11
+ * it held the thread for that work (Home Assistant 2026-10-12,
12
+ * torrent-tv/meta#178).
13
+ *
14
+ * @param {{ now?: () => number, nextTurn?: () => Promise<void> }} [options]
15
+ * @returns {() => Promise<boolean>} Resolves true when the thread was handed back.
16
+ */
17
+ export function takeTurns({ now = () => performance.now(), nextTurn = () => new Promise(resolve => setImmediate(resolve)) } = {}) {
18
+ let workingSince = now();
19
+ let othersTook = 0;
20
+ return async () => {
21
+ if (now() - workingSince < othersTook) return false;
22
+ const handedBackAt = now();
23
+ await nextTurn();
24
+ workingSince = now();
25
+ othersTook = workingSince - handedBackAt;
26
+ return true;
27
+ };
28
+ }