@torrent-tv/proxy 2.39.1 → 2.40.0

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.
@@ -0,0 +1,133 @@
1
+ /**
2
+ * @file Claiming what a reader wants in four bands instead of one.
3
+ *
4
+ * Until now a reader claimed one band at one urgency, and WebTorrent's own
5
+ * whole-file selection sat under it at priority 0 — so "what the viewer reaches
6
+ * in seconds" and "the rest of the film" were indistinguishable to the picker.
7
+ * The bands separate them, and their widths come from what has been measured
8
+ * about this file on this swarm rather than from numbers chosen here.
9
+ */
10
+
11
+ import test from "node:test";
12
+ import assert from "node:assert/strict";
13
+ import { bandWidthsFrom, bandsFrom, sameBands } from "../services/torrent-worker/piece-reader.js";
14
+
15
+ const PIECE = 8 * 1024 * 1024;
16
+
17
+ test("the urgent band keeps the highest priority, and the lead bands come behind it in order", () => {
18
+ const bands = bandsFrom({
19
+ urgent: { from: 100, to: 103 },
20
+ pieceIndex: 100,
21
+ firstPiece: 0,
22
+ lastPiece: 1000,
23
+ widths: { near: 5, far: 20 }
24
+ });
25
+
26
+ assert.deepEqual(
27
+ bands,
28
+ [
29
+ { from: 100, to: 103, priority: 4 },
30
+ { from: 104, to: 108, priority: 3 },
31
+ { from: 109, to: 128, priority: 2 }
32
+ ],
33
+ "no gap, no overlap, and each band strictly less urgent than the one in front"
34
+ );
35
+ });
36
+
37
+ test("no priority is zero, because the library's own whole-file fill sits there", () => {
38
+ const bands = bandsFrom({
39
+ urgent: { from: 0, to: 1 },
40
+ pieceIndex: 0,
41
+ firstPiece: 0,
42
+ lastPiece: 100,
43
+ widths: { near: 2, far: 2 }
44
+ });
45
+
46
+ assert.ok(
47
+ bands.every((band) => band.priority > 0),
48
+ "a band at 0 would be indistinguishable from the background fill of the whole torrent"
49
+ );
50
+ });
51
+
52
+ test("what was never downloaded behind the viewer is not asked for until the lead reaches the end", () => {
53
+ const stillFilling = bandsFrom({
54
+ urgent: { from: 50, to: 52 },
55
+ pieceIndex: 50,
56
+ firstPiece: 0,
57
+ lastPiece: 1000,
58
+ widths: { near: 4, far: 8 }
59
+ });
60
+ assert.equal(
61
+ stillFilling.length,
62
+ 3,
63
+ "the film ahead is not covered yet, so the past must not compete with it"
64
+ );
65
+
66
+ const covered = bandsFrom({
67
+ urgent: { from: 50, to: 52 },
68
+ pieceIndex: 50,
69
+ firstPiece: 0,
70
+ lastPiece: 60,
71
+ widths: { near: 4, far: 8 }
72
+ });
73
+ assert.deepEqual(
74
+ covered[covered.length - 1],
75
+ { from: 0, to: 49, priority: 1 },
76
+ "once there is nothing left ahead, the gap behind the viewer is worth filling"
77
+ );
78
+ });
79
+
80
+ test("a swarm with no surplus produces no far band, because there is nothing to get ahead with", () => {
81
+ const widths = bandWidthsFrom({
82
+ worstWaitSec: 2,
83
+ medianIntervalSec: 10,
84
+ downloadBytesPerSec: 1_000_000,
85
+ consumeBytesPerSec: 1_000_000,
86
+ pieceLength: PIECE,
87
+ basePieces: 4
88
+ });
89
+
90
+ assert.equal(widths.far, 0, "download rate equals consumption, so no lead can be built");
91
+ assert.equal(widths.measured, true);
92
+ });
93
+
94
+ test("the near band covers the worst interruption this reader has actually met", () => {
95
+ // 4 s of a film eaten at 2 MB/s is 8 MB, which is one piece here.
96
+ const widths = bandWidthsFrom({
97
+ worstWaitSec: 4,
98
+ medianIntervalSec: 30,
99
+ downloadBytesPerSec: 3_000_000,
100
+ consumeBytesPerSec: 2_000_000,
101
+ pieceLength: PIECE,
102
+ basePieces: 4
103
+ });
104
+
105
+ assert.equal(widths.near, 1, "worst wait × consumption, in pieces");
106
+ // A surplus of 1 MB/s held for 30 s is 30 MB, which is four pieces here.
107
+ assert.equal(widths.far, 4, "surplus × the interval it usually runs for, in pieces");
108
+ });
109
+
110
+ test("with nothing measured yet both lead bands fall back to the reader's own window", () => {
111
+ const widths = bandWidthsFrom({
112
+ worstWaitSec: null,
113
+ medianIntervalSec: null,
114
+ downloadBytesPerSec: 5_000_000,
115
+ consumeBytesPerSec: 0,
116
+ pieceLength: PIECE,
117
+ basePieces: 6
118
+ });
119
+
120
+ assert.deepEqual(widths, { near: 6, far: 6, measured: false });
121
+ });
122
+
123
+ test("an unchanged claim is recognised, so it is not released and re-made on every read", () => {
124
+ const bands = [{ from: 1, to: 2, priority: 4 }, { from: 3, to: 9, priority: 3 }];
125
+
126
+ assert.equal(sameBands(bands, [...bands.map((band) => ({ ...band }))]), true);
127
+ assert.equal(sameBands(bands, [{ from: 1, to: 2, priority: 4 }]), false);
128
+ assert.equal(
129
+ sameBands(bands, [{ from: 1, to: 2, priority: 4 }, { from: 3, to: 9, priority: 2 }]),
130
+ false,
131
+ "the same range at another urgency is a different claim"
132
+ );
133
+ });
@@ -0,0 +1,123 @@
1
+ /**
2
+ * @file Asking a second wire for the blocks a reader is still waiting on.
3
+ *
4
+ * Measured on a real swarm 2026-08-19: a blocked read's tail is 2-14 blocks of
5
+ * 512, and it sits on wires at 109-886 KB/s — above the 48 KB/s gate that
6
+ * WebTorrent's own `_hotswap` requires, so the library never duplicates them.
7
+ * The mechanism is the library's: free the reservation with `Piece.cancel` and
8
+ * leave the first request in flight, then ask a second wire through the
9
+ * library's own request path.
10
+ */
11
+
12
+ import test from "node:test";
13
+ import assert from "node:assert/strict";
14
+ import { duplicateTailFor } from "../services/torrent-worker/fastest-wires.js";
15
+
16
+ /** A piece with `chunks` blocks, of which `missing` at the end are absent. */
17
+ function piece(chunks, missing) {
18
+ const buffer = new Array(chunks);
19
+ for (let index = 0; index < chunks - missing; index += 1) {
20
+ buffer[index] = new Uint8Array(1);
21
+ }
22
+ const cancelled = [];
23
+ return {
24
+ _buffer: buffer,
25
+ _chunks: chunks,
26
+ cancel: (index) => cancelled.push(index),
27
+ cancelled
28
+ };
29
+ }
30
+
31
+ function wire({ speed = 500_000, has = true } = {}) {
32
+ return {
33
+ peerPieces: { get: () => has },
34
+ downloadSpeed: () => speed,
35
+ peerChoking: false,
36
+ destroyed: false,
37
+ requests: []
38
+ };
39
+ }
40
+
41
+ test("the missing blocks are freed and asked of a second wire", () => {
42
+ const target = piece(512, 3);
43
+ const placed = [];
44
+ const torrent = {
45
+ pieces: [target],
46
+ wires: [wire({ speed: 900_000 }), wire({ speed: 800_000 }), wire({ speed: 700_000 })],
47
+ _request: (wireAsked, index) => {
48
+ placed.push({ wireAsked, index });
49
+ return true;
50
+ }
51
+ };
52
+
53
+ const result = duplicateTailFor(torrent, 0);
54
+
55
+ assert.equal(result.missing, 3, "the tail is what is still absent from the piece");
56
+ assert.equal(result.duplicated, 3);
57
+ assert.deepEqual(target.cancelled, [509, 510, 511], "each block's reservation is freed first");
58
+ assert.equal(placed.length, 3, "and each is then asked of a wire through the library's own path");
59
+ assert.equal(placed[0].wireAsked, torrent.wires[0], "fastest wire first");
60
+ });
61
+
62
+ test("at most one duplicate per wire, however long the tail", () => {
63
+ const target = piece(512, 40);
64
+ const torrent = {
65
+ pieces: [target],
66
+ wires: [wire(), wire()],
67
+ _request: () => true
68
+ };
69
+
70
+ const result = duplicateTailFor(torrent, 0);
71
+
72
+ assert.equal(result.duplicated, 2, "two wires, two duplicates — the rest waits for the next attempt");
73
+ assert.equal(target.cancelled.length, 2, "and no reservation is freed that nobody was asked for");
74
+ });
75
+
76
+ test("a wire whose pipeline is full ends the attempt", () => {
77
+ const target = piece(512, 5);
78
+ const torrent = {
79
+ pieces: [target],
80
+ wires: [wire(), wire(), wire()],
81
+ // The library refuses when the wire already has as many requests as its
82
+ // measured speed justifies.
83
+ _request: () => false
84
+ };
85
+
86
+ const result = duplicateTailFor(torrent, 0);
87
+
88
+ assert.equal(result.duplicated, 0);
89
+ assert.equal(
90
+ target.cancelled.length,
91
+ 1,
92
+ "only the block that was actually offered is freed; the rest keep their reservations"
93
+ );
94
+ });
95
+
96
+ test("a piece with nothing missing is left alone", () => {
97
+ const target = piece(4, 0);
98
+ const torrent = { pieces: [target], wires: [wire()], _request: () => true };
99
+
100
+ const result = duplicateTailFor(torrent, 0);
101
+
102
+ assert.equal(result.duplicated, 0);
103
+ assert.equal(result.missing, 0);
104
+ assert.equal(target.cancelled.length, 0);
105
+ });
106
+
107
+ test("with no wire holding the piece there is nothing to duplicate onto", () => {
108
+ const target = piece(8, 2);
109
+ const torrent = { pieces: [target], wires: [wire({ has: false })], _request: () => true };
110
+
111
+ const result = duplicateTailFor(torrent, 0);
112
+
113
+ assert.equal(result.wires, 0);
114
+ assert.equal(result.duplicated, 0);
115
+ assert.equal(target.cancelled.length, 0, "and no reservation is freed for nobody");
116
+ });
117
+
118
+ test("a completed piece is not touched", () => {
119
+ assert.deepEqual(
120
+ duplicateTailFor({ pieces: [null], wires: [wire()], _request: () => true }, 0),
121
+ { duplicated: 0, missing: 0, wires: 0 }
122
+ );
123
+ });