@torrent-tv/proxy 2.97.28 → 2.97.29
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
|
@@ -59,10 +59,19 @@ export class DownloadMaps {
|
|
|
59
59
|
}
|
|
60
60
|
const deadlineAt = Number.isFinite(current.deadlineAt)
|
|
61
61
|
? current.deadlineAt + (Math.max(0, zone.from - current.from) - (zone.leadSeconds ?? 0)) * 1000 : current.deadlineAt;
|
|
62
|
-
|
|
62
|
+
// A deadline worked out again on a later clock is no change: the
|
|
63
|
+
// torrent orders by deadlines and is sent their order, which the pass
|
|
64
|
+
// below states with the rest.
|
|
65
|
+
if (Object.keys(current).some(key => key !== "from" && key !== "to" && key !== "deadlineAt" && key !== "withinSeconds" &&
|
|
66
|
+
!Object.is(zone[key], current[key]))) changed = true;
|
|
63
67
|
return [{ ...zone, ...current, from: zone.from, to: zone.to, deadlineAt }];
|
|
64
68
|
});
|
|
65
|
-
|
|
69
|
+
// THE ZONES STAND AS THEY ARE UNLESS ONE IS WITHDRAWN OR RE-WORDED. Set
|
|
70
|
+
// anew on every pass, they were a new list each time, so every metadata
|
|
71
|
+
// statement during the pass packed the whole film again: 24 s of the main
|
|
72
|
+
// thread in fifteen minutes of a two-hour AVI on Home Assistant (r24,
|
|
73
|
+
// torrent-tv/meta#178).
|
|
74
|
+
if (changed) this.#setZones(file, retained);
|
|
66
75
|
for (const [statement, metadata] of file.metadata) {
|
|
67
76
|
if (metadata.interval && !this.wantsInterval(sourceKey, fileIndex, metadata.interval)) {
|
|
68
77
|
file.metadata.delete(statement);
|
|
@@ -19,22 +19,83 @@
|
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* @returns {number}
|
|
22
|
+
* The bytes a run's input holds, each counted once: the declarations and
|
|
23
|
+
* indexes every segment of an original file names are held once by the run
|
|
24
|
+
* that reads them. Kept merged and in order, so a segment's ranges are
|
|
25
|
+
* counted against it by search rather than by sorting everything held again —
|
|
26
|
+
* a soundtrack's segments add a few bytes each, and its stretch is walked far.
|
|
28
27
|
*/
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
28
|
+
class HeldBytes {
|
|
29
|
+
/** Inclusive [start, end], merged, ascending. @type {Array<[number, number]>} */
|
|
30
|
+
#ranges = [];
|
|
31
|
+
total = 0;
|
|
32
|
+
|
|
33
|
+
/** @param {Array<[number, number]>} ranges */
|
|
34
|
+
constructor(ranges = []) {
|
|
35
|
+
this.add(ranges);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** The first held range that ends at or after `at`. */
|
|
39
|
+
#from(at) {
|
|
40
|
+
let low = 0;
|
|
41
|
+
let high = this.#ranges.length;
|
|
42
|
+
while (low < high) {
|
|
43
|
+
const middle = (low + high) >> 1;
|
|
44
|
+
if (this.#ranges[middle][1] < at) low = middle + 1;
|
|
45
|
+
else high = middle;
|
|
46
|
+
}
|
|
47
|
+
return low;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* What these ranges would add, without adding them.
|
|
52
|
+
*
|
|
53
|
+
* @param {Array<[number, number]>} ranges
|
|
54
|
+
* @returns {number}
|
|
55
|
+
*/
|
|
56
|
+
adds(ranges) {
|
|
57
|
+
let added = 0;
|
|
58
|
+
for (const [start, end] of merged(ranges)) {
|
|
59
|
+
let at = start;
|
|
60
|
+
for (let index = this.#from(start); at <= end && index < this.#ranges.length && this.#ranges[index][0] <= end; index++) {
|
|
61
|
+
const [heldStart, heldEnd] = this.#ranges[index];
|
|
62
|
+
if (heldStart > at) added += heldStart - at;
|
|
63
|
+
at = Math.max(at, heldEnd + 1);
|
|
64
|
+
}
|
|
65
|
+
if (at <= end) added += end - at + 1;
|
|
66
|
+
}
|
|
67
|
+
return added;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** @param {Array<[number, number]>} ranges */
|
|
71
|
+
add(ranges) {
|
|
72
|
+
for (const [start, end] of merged(ranges)) {
|
|
73
|
+
this.total += this.adds([[start, end]]);
|
|
74
|
+
// Joined with every held range it touches or overlaps.
|
|
75
|
+
const first = this.#from(start - 1);
|
|
76
|
+
let last = first;
|
|
77
|
+
let joinedStart = start;
|
|
78
|
+
let joinedEnd = end;
|
|
79
|
+
while (last < this.#ranges.length && this.#ranges[last][0] <= end + 1) {
|
|
80
|
+
joinedStart = Math.min(joinedStart, this.#ranges[last][0]);
|
|
81
|
+
joinedEnd = Math.max(joinedEnd, this.#ranges[last][1]);
|
|
82
|
+
last++;
|
|
83
|
+
}
|
|
84
|
+
this.#ranges.splice(first, last - first, [joinedStart, joinedEnd]);
|
|
85
|
+
}
|
|
36
86
|
}
|
|
37
|
-
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Inclusive ranges merged where they overlap or touch, ascending. @param {Array<[number, number]>} ranges */
|
|
90
|
+
function merged(ranges) {
|
|
91
|
+
const sorted = [...ranges].sort((left, right) => left[0] - right[0]);
|
|
92
|
+
const out = [];
|
|
93
|
+
for (const [start, end] of sorted) {
|
|
94
|
+
const previous = out.at(-1);
|
|
95
|
+
if (previous && start <= previous[1] + 1) previous[1] = Math.max(previous[1], end);
|
|
96
|
+
else out.push([start, end]);
|
|
97
|
+
}
|
|
98
|
+
return out;
|
|
38
99
|
}
|
|
39
100
|
|
|
40
101
|
/**
|
|
@@ -48,39 +109,56 @@ function coveredBytes(ranges) {
|
|
|
48
109
|
* @returns {Zone[]} In their order; raised zones are copies.
|
|
49
110
|
*/
|
|
50
111
|
export function expandToTargetInputs(zones, targetBytesOf) {
|
|
51
|
-
|
|
52
|
-
|
|
112
|
+
// ONE WALK FOR EACH OUTPUT'S FIRST SEGMENT, and the segments after it only
|
|
113
|
+
// as far as the run's input reaches. The map names every segment of a film,
|
|
114
|
+
// and each pass used to group all of them and work out each one's highest
|
|
115
|
+
// priority before looking at the few the run copies: 26 s of the main thread
|
|
116
|
+
// in fifteen minutes of a two-hour AVI on Home Assistant (r24,
|
|
117
|
+
// torrent-tv/meta#178).
|
|
118
|
+
/** @type {Map<string, { index: number, priority: number, zone: Zone }>} */
|
|
119
|
+
const first = new Map();
|
|
53
120
|
for (const zone of zones) {
|
|
54
|
-
|
|
55
|
-
if (!
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
else segments.set(zone.index, [zone]);
|
|
121
|
+
const best = first.get(zone.outputKey);
|
|
122
|
+
if (!best || zone.priority > best.priority || (zone.priority === best.priority && zone.index < best.index)) {
|
|
123
|
+
first.set(zone.outputKey, { index: zone.index, priority: zone.priority, zone });
|
|
124
|
+
}
|
|
59
125
|
}
|
|
60
|
-
/** @type {Map<Zone, Zone>} */
|
|
61
|
-
const
|
|
62
|
-
for (const [outputKey,
|
|
126
|
+
/** @type {Map<string, { first: { index: number, priority: number, zone: Zone }, target: number, after: Map<number, Zone[]> }>} */
|
|
127
|
+
const runs = new Map();
|
|
128
|
+
for (const [outputKey, best] of first) {
|
|
63
129
|
const target = targetBytesOf(outputKey);
|
|
64
|
-
if (
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
130
|
+
if (target > 0) runs.set(outputKey, { first: best, target, after: new Map() });
|
|
131
|
+
}
|
|
132
|
+
if (runs.size === 0) return zones;
|
|
133
|
+
/** @type {Map<string, Array<[number, number]>>} */
|
|
134
|
+
const held = new Map();
|
|
135
|
+
for (const zone of zones) {
|
|
136
|
+
const run = runs.get(zone.outputKey);
|
|
137
|
+
if (!run) continue;
|
|
138
|
+
if (zone.index === run.first.index) {
|
|
139
|
+
let ranges = held.get(zone.outputKey);
|
|
140
|
+
if (!ranges) held.set(zone.outputKey, ranges = []);
|
|
141
|
+
ranges.push([zone.byteStart, zone.byteEnd]);
|
|
142
|
+
} else if (zone.index > run.first.index) {
|
|
143
|
+
const list = run.after.get(zone.index);
|
|
144
|
+
if (list) list.push(zone);
|
|
145
|
+
else run.after.set(zone.index, [zone]);
|
|
71
146
|
}
|
|
72
|
-
|
|
73
|
-
|
|
147
|
+
}
|
|
148
|
+
/** @type {Map<Zone, Zone>} */
|
|
149
|
+
const raised = new Map();
|
|
150
|
+
for (const [outputKey, run] of runs) {
|
|
151
|
+
const holding = new HeldBytes(held.get(outputKey) ?? []);
|
|
74
152
|
// The run's input is a stretch of consecutive segments: one already made,
|
|
75
153
|
// or not yet worked out, ends it.
|
|
76
|
-
for (let index = first.index + 1;
|
|
77
|
-
const list =
|
|
78
|
-
const ranges =
|
|
79
|
-
if (
|
|
80
|
-
|
|
154
|
+
for (let index = run.first.index + 1; run.after.has(index); index++) {
|
|
155
|
+
const list = run.after.get(index);
|
|
156
|
+
const ranges = list.map(zone => [zone.byteStart, zone.byteEnd]);
|
|
157
|
+
if (holding.total + holding.adds(ranges) > run.target) break;
|
|
158
|
+
holding.add(ranges);
|
|
81
159
|
for (const zone of list) {
|
|
82
|
-
raised.set(zone, { ...zone, priority: first.priority, urgent: first.zone.urgent,
|
|
83
|
-
behind: first.zone.behind, deferred: first.zone.deferred });
|
|
160
|
+
raised.set(zone, { ...zone, priority: run.first.priority, urgent: run.first.zone.urgent,
|
|
161
|
+
behind: run.first.zone.behind, deferred: run.first.zone.deferred });
|
|
84
162
|
}
|
|
85
163
|
}
|
|
86
164
|
}
|