@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@torrent-tv/proxy",
3
- "version": "2.96.10",
3
+ "version": "2.96.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": {
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
- return converted;
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 { isAtAWatchingViewer, isBehindEverybody, isNobodyComingNow } from "../viewer/PriorityMap.js";
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
- * Which level of urgency one zone is stated at, from the map's own number
1295
- * and nothing else. The map's numbers are a scale as long as the film needs
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: Number.isFinite(zone.priority) ? Math.max(1, Math.min(100, zone.priority)) : 1,
1309
+ priority: priorityOfMapZone(zone),
1349
1310
  order: Number.isSafeInteger(zone.order) && zone.order >= 0 ? zone.order : 0,
1350
- deadlineAt: Number.isFinite(zone.deadlineAt) ? zone.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
  });