@torrent-tv/proxy 2.96.10 → 2.96.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 +1 -1
- package/server.js +4 -1
- package/services/torrent/map-zones.js +91 -0
- package/services/torrent/torrent-pool.js +6 -46
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -55,6 +55,7 @@ import { createPlaybackPlanner } from "./services/media/playback-planner.js";
|
|
|
55
55
|
import { KeyframeTables } from "./services/media/KeyframeTables.js";
|
|
56
56
|
import { contentsOf } from "./services/torrent/Contents.js";
|
|
57
57
|
import { joinedWithinPieces } from "./services/torrent/demand/pieces.js";
|
|
58
|
+
import { withoutRepeatedZones } from "./services/torrent/map-zones.js";
|
|
58
59
|
import { SubtitleOrchestrator } from "./services/media/SubtitleOrchestrator.js";
|
|
59
60
|
import { containerOrchestrator, CONTAINER_HEAD_BYTES, describeWorkTags } from "./services/media/ContainerOrchestrator.js";
|
|
60
61
|
import { readPlaybackDeclarations } from "./services/media/read-playback-declarations.js";
|
|
@@ -533,7 +534,9 @@ export async function startProxyServer({
|
|
|
533
534
|
segmentSourceRanges.set(rangesKey, input.ranges);
|
|
534
535
|
convert(input.ranges);
|
|
535
536
|
}
|
|
536
|
-
|
|
537
|
+
// The ranges a picture reads at open are named again by every segment;
|
|
538
|
+
// only the zone the swarm would rank first is sent (torrent-tv/meta#166).
|
|
539
|
+
return withoutRepeatedZones(converted);
|
|
537
540
|
}
|
|
538
541
|
});
|
|
539
542
|
const mediaReads = new MediaReadRequests({
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file What one zone of the priority map tells the torrent: the level of
|
|
3
|
+
* urgency it is stated at, and the band the swarm ranks it by.
|
|
4
|
+
*
|
|
5
|
+
* Pure arithmetic over the zone's own fields, so the main thread can ask the
|
|
6
|
+
* same question before a map is sent and the torrent thread when it is applied.
|
|
7
|
+
*/
|
|
8
|
+
import { isAtAWatchingViewer, isBehindEverybody, isNobodyComingNow } from "../viewer/PriorityMap.js";
|
|
9
|
+
import { Urgency } from "./demand/Urgency.js";
|
|
10
|
+
import { compareBands } from "./download/bands.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Which level of urgency one zone is stated at, from the map's own number
|
|
14
|
+
* and nothing else. The map's numbers are a scale as long as the film needs
|
|
15
|
+
* — ten bands on a fifty-minute film — while the register has five levels,
|
|
16
|
+
* and the fit is by meaning:
|
|
17
|
+
*
|
|
18
|
+
* - the top of the scale is where a viewer is standing, so it is the
|
|
19
|
+
* cushion being built: {@link Urgency.NEAR}. Never BLOCKED — that level
|
|
20
|
+
* means a reader is stopped on those bytes right now, which only a read
|
|
21
|
+
* can say;
|
|
22
|
+
* - the bottom is what nobody is approaching: behind a viewer moving
|
|
23
|
+
* forward, and the whole film of a viewer who has stopped the picture.
|
|
24
|
+
* Wanted only if somebody seeks back, which is {@link Urgency.BEHIND};
|
|
25
|
+
* - one above the bottom is the far tail — wanted for certain if the
|
|
26
|
+
* viewer watches on, and not before: {@link Urgency.TAIL};
|
|
27
|
+
* - everything between is the lead being built: {@link Urgency.AHEAD}.
|
|
28
|
+
*
|
|
29
|
+
* Read from the scale's own ends rather than from the highest and lowest
|
|
30
|
+
* number in THIS file's map. The two speculative levels are withheld across
|
|
31
|
+
* every torrent at once while anything urgent is missing anywhere, so a
|
|
32
|
+
* paused viewer's film has to compare as wanted-last against another film
|
|
33
|
+
* somebody is watching — and relative to itself alone it would compare as
|
|
34
|
+
* the most urgent thing there is.
|
|
35
|
+
*
|
|
36
|
+
* @param {{ priority?: number, behind?: boolean, deferred?: boolean, urgent?: boolean }} zone
|
|
37
|
+
* @returns {number}
|
|
38
|
+
*/
|
|
39
|
+
export function levelOfMapZone(zone) {
|
|
40
|
+
// Read through the map's own words rather than by comparing its numbers.
|
|
41
|
+
// The scale is that layer's, and the numbers inside a band mean nothing
|
|
42
|
+
// but their order.
|
|
43
|
+
const priority = zone.priority ?? 0;
|
|
44
|
+
if (zone.behind === true || (zone.behind === undefined && isBehindEverybody(priority))) {
|
|
45
|
+
return Urgency.BEHIND;
|
|
46
|
+
}
|
|
47
|
+
if (zone.deferred === true || (zone.deferred === undefined && isNobodyComingNow(priority))) {
|
|
48
|
+
// In front of somebody who has stopped the picture, and of nobody who is
|
|
49
|
+
// watching. Wanted, and wanted after everyone who is on their way.
|
|
50
|
+
return Urgency.TAIL;
|
|
51
|
+
}
|
|
52
|
+
return (zone.urgent === true || (zone.urgent === undefined && isAtAWatchingViewer(priority))) ? Urgency.NEAR : Urgency.AHEAD;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** The priority a zone is stated at, within the register's scale. */
|
|
56
|
+
export function priorityOfMapZone(zone) {
|
|
57
|
+
return Number.isFinite(zone.priority) ? Math.max(1, Math.min(100, zone.priority)) : 1;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** When a zone's bytes are needed, as an instant; `now` reads `withinSeconds`. */
|
|
61
|
+
export function deadlineOfMapZone(zone, now = Date.now()) {
|
|
62
|
+
return Number.isFinite(zone.deadlineAt) ? zone.deadlineAt :
|
|
63
|
+
Number.isFinite(zone.withinSeconds) ? now + Math.max(0, zone.withinSeconds) * 1000 : Number.POSITIVE_INFINITY;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The zones with every repeat of the same bytes left out but the one the swarm
|
|
68
|
+
* would rank first.
|
|
69
|
+
*
|
|
70
|
+
* Each piece is fetched in the band of the best zone that holds it
|
|
71
|
+
* ({@link compareBands}), so a zone naming exactly the bytes of a better one
|
|
72
|
+
* changes nothing the swarm is told. The ranges an AVI's picture reads at open
|
|
73
|
+
* — its declarations and its indexes — are named again in the zone of every
|
|
74
|
+
* segment: field 2026-10-10 (torrent-tv/meta#166), maps of 9700 zones, for a
|
|
75
|
+
* film of 2035 segments, cost half a second each to deliver.
|
|
76
|
+
*
|
|
77
|
+
* @template {{ byteStart: number, byteEnd: number }} Zone
|
|
78
|
+
* @param {Zone[]} zones
|
|
79
|
+
* @param {number} [now]
|
|
80
|
+
* @returns {Zone[]} In their order, without the repeats.
|
|
81
|
+
*/
|
|
82
|
+
export function withoutRepeatedZones(zones, now = Date.now()) {
|
|
83
|
+
const bandOf = zone => ({ urgency: levelOfMapZone(zone), priority: priorityOfMapZone(zone), deadlineAt: deadlineOfMapZone(zone, now) });
|
|
84
|
+
const best = new Map();
|
|
85
|
+
zones.forEach((zone, index) => {
|
|
86
|
+
const key = `${zone.byteStart}:${zone.byteEnd}`;
|
|
87
|
+
const kept = best.get(key);
|
|
88
|
+
if (!kept || compareBands(bandOf(zone), kept.band) < 0) best.set(key, { index, band: bandOf(zone) });
|
|
89
|
+
});
|
|
90
|
+
return zones.filter((zone, index) => best.get(`${zone.byteStart}:${zone.byteEnd}`).index === index);
|
|
91
|
+
}
|
|
@@ -20,7 +20,7 @@ import { demandProgress } from "./demand-progress.js";
|
|
|
20
20
|
import { Urgency, urgencyName } from "./demand/index.js";
|
|
21
21
|
import { demandFor, firstClassStatement, forgetTorrent, reconcileAll, hasUnmetDemand } from "./download/registry.js";
|
|
22
22
|
import { withdrawClaim } from "./download/withdraw-claim.js";
|
|
23
|
-
import {
|
|
23
|
+
import { deadlineOfMapZone, levelOfMapZone, priorityOfMapZone } from "./map-zones.js";
|
|
24
24
|
import { deriveSourceKey } from "../../utils/torrent-source-key.js";
|
|
25
25
|
import { noteTorrentDestroyed } from "./destroyed-torrents.js";
|
|
26
26
|
|
|
@@ -1290,48 +1290,9 @@ export class TorrentPool {
|
|
|
1290
1290
|
.filter((zone) => Number.isSafeInteger(zone?.byteStart) && Number.isSafeInteger(zone?.byteEnd) &&
|
|
1291
1291
|
zone.byteStart >= 0 && zone.byteEnd >= zone.byteStart && zone.byteStart < length)
|
|
1292
1292
|
.sort((left, right) => (right.priority ?? 0) - (left.priority ?? 0));
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
* — ten bands on a fifty-minute film — while the register has five levels,
|
|
1297
|
-
* and the fit is by meaning:
|
|
1298
|
-
*
|
|
1299
|
-
* - the top of the scale is where a viewer is standing, so it is the
|
|
1300
|
-
* cushion being built: {@link Urgency.NEAR}. Never BLOCKED — that level
|
|
1301
|
-
* means a reader is stopped on those bytes right now, which only a read
|
|
1302
|
-
* can say;
|
|
1303
|
-
* - the bottom is what nobody is approaching: behind a viewer moving
|
|
1304
|
-
* forward, and the whole film of a viewer who has stopped the picture.
|
|
1305
|
-
* Wanted only if somebody seeks back, which is {@link Urgency.BEHIND};
|
|
1306
|
-
* - one above the bottom is the far tail — wanted for certain if the
|
|
1307
|
-
* viewer watches on, and not before: {@link Urgency.TAIL};
|
|
1308
|
-
* - everything between is the lead being built: {@link Urgency.AHEAD}.
|
|
1309
|
-
*
|
|
1310
|
-
* Read from the scale's own ends rather than from the highest and lowest
|
|
1311
|
-
* number in THIS file's map. The two speculative levels are withheld across
|
|
1312
|
-
* every torrent at once while anything urgent is missing anywhere, so a
|
|
1313
|
-
* paused viewer's film has to compare as wanted-last against another film
|
|
1314
|
-
* somebody is watching — and relative to itself alone it would compare as
|
|
1315
|
-
* the most urgent thing there is.
|
|
1316
|
-
*
|
|
1317
|
-
* @param {{ priority?: number }} zone
|
|
1318
|
-
* @returns {number}
|
|
1319
|
-
*/
|
|
1320
|
-
const levelOf = (zone) => {
|
|
1321
|
-
// Read through the map's own words rather than by comparing its numbers.
|
|
1322
|
-
// The scale is that layer's, and the numbers inside a band mean nothing
|
|
1323
|
-
// but their order.
|
|
1324
|
-
const priority = zone.priority ?? 0;
|
|
1325
|
-
if (zone.behind === true || (zone.behind === undefined && isBehindEverybody(priority))) {
|
|
1326
|
-
return Urgency.BEHIND;
|
|
1327
|
-
}
|
|
1328
|
-
if (zone.deferred === true || (zone.deferred === undefined && isNobodyComingNow(priority))) {
|
|
1329
|
-
// In front of somebody who has stopped the picture, and of nobody who is
|
|
1330
|
-
// watching. Wanted, and wanted after everyone who is on their way.
|
|
1331
|
-
return Urgency.TAIL;
|
|
1332
|
-
}
|
|
1333
|
-
return (zone.urgent === true || (zone.urgent === undefined && isAtAWatchingViewer(priority))) ? Urgency.NEAR : Urgency.AHEAD;
|
|
1334
|
-
};
|
|
1293
|
+
// Which level each zone is stated at: `map-zones.js`, where the main thread
|
|
1294
|
+
// reads the same rule before it sends a map.
|
|
1295
|
+
const levelOf = levelOfMapZone;
|
|
1335
1296
|
ordered.forEach((zone, index) => {
|
|
1336
1297
|
// Only ranges declared by the container index or metadata read are wanted.
|
|
1337
1298
|
const byteStart = zone.byteStart;
|
|
@@ -1345,10 +1306,9 @@ export class TorrentPool {
|
|
|
1345
1306
|
byteStart,
|
|
1346
1307
|
byteEnd,
|
|
1347
1308
|
urgency: levelOf(zone),
|
|
1348
|
-
priority:
|
|
1309
|
+
priority: priorityOfMapZone(zone),
|
|
1349
1310
|
order: Number.isSafeInteger(zone.order) && zone.order >= 0 ? zone.order : 0,
|
|
1350
|
-
deadlineAt:
|
|
1351
|
-
Number.isFinite(zone.withinSeconds) ? Date.now() + Math.max(0, zone.withinSeconds) * 1000 : Number.POSITIVE_INFINITY,
|
|
1311
|
+
deadlineAt: deadlineOfMapZone(zone),
|
|
1352
1312
|
requestId: zone.requestId ?? `${torrent.infoHash}:${fileIndex}:map`
|
|
1353
1313
|
});
|
|
1354
1314
|
});
|