@torrent-tv/proxy 2.99.10 → 2.99.11

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.99.10",
3
+ "version": "2.99.11",
4
4
  "description": "Torrent proxy client that exposes webseed-like HTTP stream endpoint.",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "repository": {
@@ -30,19 +30,44 @@ let publishingSelections = false;
30
30
  /** What the last reconciliation told the swarm to fetch first, for the pool to say. */
31
31
  let firstClassStated = "none";
32
32
 
33
- /** Concurrent file reports share one snapshot of every live download map. */
33
+ /** The last forecast that finished on the maps it was begun on, and how to tell it still describes them. */
34
+ let latestForecast = null;
35
+
36
+ /**
37
+ * The arrival forecast of every live download map, shared by concurrent file
38
+ * reports.
39
+ *
40
+ * ANSWERED WITH THE LAST ONE FINISHED. Each forecast states when it was made
41
+ * (`measuredAt`), and a newer one is worked out beside the answer whenever the
42
+ * maps or the held pieces have changed since, one at a time. Awaited afresh on
43
+ * every report, it held a waiting viewer's answer 3-11.6 s on Home Assistant
44
+ * while it took about a quarter of a second of work, the machine 83-94 % busy:
45
+ * the page asks again only once answered, so the hold after a seek to 100 s
46
+ * lasted 52.5 s (r51, torrent-tv/meta#178). Only the first report waits, when
47
+ * there is no forecast yet. A forecast whose maps change while it is worked out
48
+ * is not kept.
49
+ *
50
+ * @returns {Promise<Map<object, { measuredAt: number, arrivals: Map<number, number | null> }>>}
51
+ */
34
52
  export function forecastDownloads() {
35
- if (!futurePending) {
53
+ if (!futurePending && !latestForecast?.isCurrent()) {
36
54
  const entries = [...live];
37
55
  const revisions = entries.map(entry => entry.register.revision);
38
56
  const withdrawals = entries.map(entry => entry.withdrawalRevision);
57
+ const verified = entries.map(entry => entry.verifiedRevision);
39
58
  const isCurrent = () => entries.length === live.size && !entries.some((entry, index) =>
40
59
  !live.has(entry) || entry.register.revision !== revisions[index] ||
41
60
  entry.withdrawalRevision !== withdrawals[index]);
42
- futurePending = futureDownload(entries, { isCurrent }).then(result =>
43
- isCurrent() ? result : new Map()).finally(() => { futurePending = null; });
61
+ // Kept while no piece has arrived either: a piece forecast to come has
62
+ // either come, or its time is past and it must be forecast again.
63
+ const describes = () => isCurrent() && entries.every((entry, index) => entry.verifiedRevision === verified[index]);
64
+ futurePending = futureDownload(entries, { isCurrent }).then(result => {
65
+ if (!isCurrent()) return new Map();
66
+ latestForecast = { result, isCurrent: describes };
67
+ return result;
68
+ }).finally(() => { futurePending = null; });
44
69
  }
45
- return futurePending;
70
+ return latestForecast ? Promise.resolve(latestForecast.result) : futurePending;
46
71
  }
47
72
 
48
73
  /**
@@ -58,10 +83,11 @@ export function demandFor(torrent) {
58
83
  }
59
84
  const register = new DemandRegister();
60
85
  const selection = new SwarmSelection({ torrent, register });
61
- const entry = { torrent, register, selection, limit: new SelectionLimit({ torrent, register, selection }), withdrawalRevision: 0 };
86
+ const entry = { torrent, register, selection, limit: new SelectionLimit({ torrent, register, selection }), withdrawalRevision: 0,
87
+ verifiedRevision: 0 };
62
88
  byTorrent.set(torrent, entry);
63
89
  live.add(entry);
64
- entry.onBytesChanged = () => reconcileAll();
90
+ entry.onBytesChanged = () => { entry.verifiedRevision++; reconcileAll(); };
65
91
  entry.onWithdrawn = () => { entry.withdrawalRevision++; reconcileAll(); };
66
92
  torrent.on?.("verified", entry.onBytesChanged);
67
93
  torrent.on?.("piece-withdrawn", entry.onWithdrawn);