@torrent-tv/proxy 2.93.0 → 2.93.1

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.93.0",
3
+ "version": "2.93.1",
4
4
  "description": "Torrent proxy client that exposes webseed-like HTTP stream endpoint.",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "repository": {
@@ -46,6 +46,11 @@ export function peerCanServe(wire, piece) {
46
46
  wire.peerPieces?.get(piece);
47
47
  }
48
48
 
49
+ function peerCanRequest(wire) {
50
+ return !wire.destroyed &&
51
+ (!wire.peerChoking || (wire.hasFast && wire.peerAllowedFastSet?.length > 0));
52
+ }
53
+
49
54
  /** Identical map ordering for present requests and conditional future requests. */
50
55
  export function compareDownloadCandidates(a, b) {
51
56
  return a.deadlineAt - b.deadlineAt || b.priority - a.priority || a.order - b.order || a.piece - b.piece;
@@ -62,11 +67,18 @@ export class DeadlineScheduler {
62
67
  this.#store = findStore;
63
68
  }
64
69
 
65
- reconcile(now = Date.now()) {
70
+ reconcile(now = Date.now(), { torrent = null, wire = null } = {}) {
66
71
  if (this.#running) return { requested: 0, reassigned: 0 };
72
+ // WebTorrent updates each peer in turn. A full or choked peer cannot issue
73
+ // a request, so its heartbeat must not scan every file's byte availability.
74
+ if (wire && (!peerCanRequest(wire) ||
75
+ (wire.requests?.length ?? 0) >= peerRequestCapacity(torrent, wire))) {
76
+ return { requested: 0, reassigned: 0 };
77
+ }
67
78
  this.#running = true;
68
79
  try {
69
- return dispatchDownloadCandidates(downloadCandidates(this.#entries(), this.#store), now);
80
+ const entries = [...this.#entries()].filter(entry => !torrent || entry.torrent === torrent);
81
+ return dispatchDownloadCandidates(downloadCandidates(entries, this.#store), now, { wire });
70
82
  } finally {
71
83
  this.#running = false;
72
84
  }
@@ -112,7 +124,7 @@ export function downloadCandidates(entries, findStore = pieceStoreOf) {
112
124
  }
113
125
 
114
126
  /** Run against protocol peers or an isolated future-state copy. */
115
- export function dispatchDownloadCandidates(candidates, now) {
127
+ export function dispatchDownloadCandidates(candidates, now, { wire: changedWire = null } = {}) {
116
128
  let requested = 0, reassigned = 0;
117
129
  // Rates and advertised limits cannot change during this synchronous pass.
118
130
  // Read each peer once, and only inspect torrents once regardless of map size.
@@ -125,15 +137,14 @@ export function dispatchDownloadCandidates(candidates, now) {
125
137
  }
126
138
  return reading;
127
139
  };
128
- const usable = wire => !wire.destroyed &&
129
- (!wire.peerChoking || (wire.hasFast && wire.peerAllowedFastSet?.length > 0));
130
- const available = (torrent, wire) => usable(wire) &&
140
+ const available = (torrent, wire) => peerCanRequest(wire) &&
131
141
  (wire.requests?.length ?? 0) < readingOf(torrent, wire).capacity;
132
142
  const completionAt = (torrent, wire, bytes) =>
133
143
  requestCompletionAt(wire, bytes, now, readingOf(torrent, wire).speed);
134
144
  const writable = new Map();
135
145
  for (const torrent of new Set(candidates.map(candidate => candidate.torrent))) {
136
- const peers = (torrent.wires ?? []).filter(wire => available(torrent, wire));
146
+ const peers = (changedWire ? [changedWire] : torrent.wires ?? [])
147
+ .filter(wire => available(torrent, wire));
137
148
  if (peers.length) writable.set(torrent, peers);
138
149
  }
139
150
  for (const candidate of candidates) {
@@ -58,8 +58,8 @@ export function demandFor(torrent) {
58
58
  const entry = { torrent, register, selection: new SwarmSelection({ torrent, register }), withdrawalRevision: 0 };
59
59
  if (typeof torrent._request === "function" && typeof torrent._updateWire === "function") {
60
60
  entry.previousWireUpdate = torrent._updateWire;
61
- torrent._updateWire = () => {
62
- if (!publishingSelections) scheduler.reconcile();
61
+ torrent._updateWire = wire => {
62
+ if (!publishingSelections) scheduler.reconcile(Date.now(), { torrent, wire });
63
63
  };
64
64
  }
65
65
  byTorrent.set(torrent, entry);