@torrent-tv/proxy 2.93.0 → 2.93.2
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
|
@@ -982,7 +982,8 @@ export function wireOutputs({
|
|
|
982
982
|
index >= window.from && index <= window.to && window.withinSeconds === 0),
|
|
983
983
|
changed: (output, result) => {
|
|
984
984
|
if (result.kind === "terminal") {
|
|
985
|
-
logger.warn(`encode input output=${output.outputKey} terminal=${result.reason}: ${result.message ?? ""}`
|
|
985
|
+
logger.warn(`encode input output=${output.outputKey} terminal=${result.reason}: ${result.message ?? ""}` +
|
|
986
|
+
(Number.isFinite(result.bytes) ? ` bytes=${result.bytes} capacity=${result.capacity}` : ""));
|
|
986
987
|
parts.serving.invalidateWaits(output);
|
|
987
988
|
}
|
|
988
989
|
parts.encodeRuns.planEncodersSoon();
|
|
@@ -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
|
-
|
|
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
|
|
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 ?? [])
|
|
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);
|
|
@@ -27,7 +27,7 @@ export class DownloadMaps {
|
|
|
27
27
|
if (!current || Object.keys(current).some(key => key !== "from" && key !== "to" && !Object.is(zone[key], key === "deadlineAt" ? deadlineAt : current[key]))) changed = true;
|
|
28
28
|
return current ? [{ ...zone, ...current, from: zone.from, to: zone.to, deadlineAt }] : [];
|
|
29
29
|
});
|
|
30
|
-
file
|
|
30
|
+
this.#setZones(file, retained);
|
|
31
31
|
for (const [statement, metadata] of file.metadata) {
|
|
32
32
|
if (metadata.interval && !this.wantsInterval(sourceKey, fileIndex, metadata.interval)) {
|
|
33
33
|
file.metadata.delete(statement);
|
|
@@ -47,7 +47,7 @@ export class DownloadMaps {
|
|
|
47
47
|
const resolved = await this.#resolvePlayback({ sourceKey, fileIndex, durationSeconds, zones: file.sourceZones,
|
|
48
48
|
isCurrent: () => this.#files.get(`${sourceKey}:${fileIndex}`) === file && revision === file.revision });
|
|
49
49
|
if (revision !== file.revision) return;
|
|
50
|
-
file
|
|
50
|
+
this.#setZones(file, resolved);
|
|
51
51
|
return this.#emit(file);
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -87,10 +87,10 @@ export class DownloadMaps {
|
|
|
87
87
|
const sources = [];
|
|
88
88
|
for (const file of this.#files.values()) {
|
|
89
89
|
if (file.sourceKey !== sourceKey) continue;
|
|
90
|
-
const
|
|
91
|
-
if (!
|
|
90
|
+
const ranges = file.inputs.get(outputKey)?.get(index);
|
|
91
|
+
if (!ranges?.length) continue;
|
|
92
92
|
sources.push({ sourceId: `${sourceKey}:${file.fileIndex}`,
|
|
93
|
-
ranges
|
|
93
|
+
ranges });
|
|
94
94
|
}
|
|
95
95
|
return sources.length ? sources : null;
|
|
96
96
|
}
|
|
@@ -130,7 +130,7 @@ export class DownloadMaps {
|
|
|
130
130
|
|
|
131
131
|
forget(sourceKey, fileIndex, { keepPreparation = false } = {}) {
|
|
132
132
|
const file = this.#file(sourceKey, fileIndex);
|
|
133
|
-
file
|
|
133
|
+
this.#setZones(file, []);
|
|
134
134
|
file.sourceZones = [];
|
|
135
135
|
if (!keepPreparation) file.nativeZones = [];
|
|
136
136
|
file.revision++;
|
|
@@ -158,12 +158,25 @@ export class DownloadMaps {
|
|
|
158
158
|
const key = `${sourceKey}:${fileIndex}`;
|
|
159
159
|
let file = this.#files.get(key);
|
|
160
160
|
if (!file) {
|
|
161
|
-
file = { sourceKey, fileIndex, durationSeconds: 0, zones: [], nativeZones: [], sourceZones: [], revision: 0, epoch: ++this.#nextEpoch, metadata: new Map(), pending: Promise.resolve() };
|
|
161
|
+
file = { sourceKey, fileIndex, durationSeconds: 0, zones: [], inputs: new Map(), nativeZones: [], sourceZones: [], revision: 0, epoch: ++this.#nextEpoch, metadata: new Map(), pending: Promise.resolve() };
|
|
162
162
|
this.#files.set(key, file);
|
|
163
163
|
}
|
|
164
164
|
return file;
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
+
#setZones(file, zones) {
|
|
168
|
+
file.zones = zones;
|
|
169
|
+
file.inputs = new Map();
|
|
170
|
+
for (const zone of zones) {
|
|
171
|
+
if (typeof zone.outputKey !== "string" || !Number.isSafeInteger(zone.index)) continue;
|
|
172
|
+
let output = file.inputs.get(zone.outputKey);
|
|
173
|
+
if (!output) file.inputs.set(zone.outputKey, output = new Map());
|
|
174
|
+
let ranges = output.get(zone.index);
|
|
175
|
+
if (!ranges) output.set(zone.index, ranges = []);
|
|
176
|
+
ranges.push({ start: zone.byteStart, end: zone.byteEnd + 1 });
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
167
180
|
#emit(file) {
|
|
168
181
|
const zones = [...file.zones, ...file.nativeZones];
|
|
169
182
|
for (const { ranges, requestId, priority, urgent, deadlineAt, order } of file.metadata.values()) {
|