@torrent-tv/proxy 2.99.13 → 2.99.14
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
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
import { readVint } from "./ebml-reader.js";
|
|
22
|
+
import { isUnavailable } from "./unavailable.js";
|
|
22
23
|
|
|
23
24
|
/** An element id is at most four bytes and a size at most eight (RFC 8794 §4). */
|
|
24
25
|
export const MAX_HEADER_BYTES = 12;
|
|
@@ -64,6 +65,10 @@ export class ElementReader {
|
|
|
64
65
|
/** @type {number} */
|
|
65
66
|
#windowAt = 0;
|
|
66
67
|
|
|
68
|
+
/** Below this position a portion was found missing: read exactly there. */
|
|
69
|
+
#exactBefore = -1;
|
|
70
|
+
#narrow;
|
|
71
|
+
|
|
67
72
|
/**
|
|
68
73
|
* @param {object} params
|
|
69
74
|
* @param {(start: number, end: number) => Promise<Buffer>} params.read - Answers
|
|
@@ -79,8 +84,13 @@ export class ElementReader {
|
|
|
79
84
|
* last byte that can be read from `start` in one go. A read of what is
|
|
80
85
|
* downloaded must not run a prefetch past the end of the downloaded run,
|
|
81
86
|
* or a cluster that is whole would fail for the bytes after it.
|
|
87
|
+
* @param {boolean} [params.narrowWhenMissing] - When a prefetched portion is
|
|
88
|
+
* not all downloaded, read only the bytes asked for, and read exactly up to
|
|
89
|
+
* the end of that portion: what is not downloaded yet is then only what is
|
|
90
|
+
* needed, and the bytes before it are read.
|
|
82
91
|
*/
|
|
83
|
-
constructor({ read, fileSize, portionBytes = Number.POSITIVE_INFINITY, prefetch = false, readableUntil }) {
|
|
92
|
+
constructor({ read, fileSize, portionBytes = Number.POSITIVE_INFINITY, prefetch = false, readableUntil, narrowWhenMissing = false }) {
|
|
93
|
+
this.#narrow = narrowWhenMissing;
|
|
84
94
|
this.#read = read;
|
|
85
95
|
this.#fileSize = fileSize;
|
|
86
96
|
this.#readableUntil = typeof readableUntil === "function" ? readableUntil : () => fileSize;
|
|
@@ -110,8 +120,15 @@ export class ElementReader {
|
|
|
110
120
|
return window.subarray(start - this.#windowAt, end - this.#windowAt);
|
|
111
121
|
}
|
|
112
122
|
const reach = Math.min(this.#fileSize, Math.max(end, this.#readableUntil(start)));
|
|
113
|
-
const span = this.#prefetch ? Math.max(end - start, Math.min(this.#portion, reach - start)) : end - start;
|
|
114
|
-
|
|
123
|
+
const span = this.#prefetch && start >= this.#exactBefore ? Math.max(end - start, Math.min(this.#portion, reach - start)) : end - start;
|
|
124
|
+
let got;
|
|
125
|
+
try {
|
|
126
|
+
got = await this.#read(start, start + span - 1);
|
|
127
|
+
} catch (error) {
|
|
128
|
+
if (!this.#narrow || span === end - start || !isUnavailable(error)) throw error;
|
|
129
|
+
this.#exactBefore = start + span;
|
|
130
|
+
got = await this.#read(start, end - 1);
|
|
131
|
+
}
|
|
115
132
|
this.#window = got;
|
|
116
133
|
this.#windowAt = start;
|
|
117
134
|
return got.subarray(0, end - start);
|
|
@@ -43,7 +43,16 @@ function recordedTracks(tracks, trackIds) {
|
|
|
43
43
|
* @returns {Promise<"covered" | "until" | "end">}
|
|
44
44
|
*/
|
|
45
45
|
export async function walkMatroskaPackets({ readRange, fileSize, portionBytes, layout, tracks, trackIds, interval, packetMemory, state, until = null, isCovered = null }) {
|
|
46
|
-
|
|
46
|
+
// INSIDE A CLUSTER EVERY BLOCK HEADER IS READ, so its bytes are read a
|
|
47
|
+
// portion at a time up to the cluster's end, and never past it. Read a
|
|
48
|
+
// header at a time, each a read of a few bytes from the torrent thread, the
|
|
49
|
+
// soundtrack's second piece of a copied 1080p film took 29.6 s on Home
|
|
50
|
+
// Assistant while that thread was downloading (r52, torrent-tv/meta#178).
|
|
51
|
+
// Between clusters only their headers are read; where a portion is not all
|
|
52
|
+
// downloaded, only the bytes a header needs are read and asked for.
|
|
53
|
+
let readableUntil = null;
|
|
54
|
+
const reader = new ElementReader({ read: readRange, fileSize, portionBytes, prefetch: true, narrowWhenMissing: true,
|
|
55
|
+
readableUntil: start => readableUntil ?? start });
|
|
47
56
|
const declared = recordedTracks(tracks, trackIds);
|
|
48
57
|
if (!state.packets) {
|
|
49
58
|
state.packets = new Map([...declared.keys()].map(id => [id, new PacketRecords(packetMemory)]));
|
|
@@ -64,6 +73,7 @@ export async function walkMatroskaPackets({ readRange, fileSize, portionBytes, l
|
|
|
64
73
|
}
|
|
65
74
|
const limit = element.end ?? layout.segmentEnd;
|
|
66
75
|
if (!state.cluster) state.cluster = { element, cursor: element.dataOffset, clusterTicks: null };
|
|
76
|
+
readableUntil = limit;
|
|
67
77
|
let { cursor, clusterTicks } = state.cluster;
|
|
68
78
|
while (cursor < limit) {
|
|
69
79
|
const child = await validHeader(reader, cursor, limit);
|
|
@@ -120,6 +130,7 @@ export async function walkMatroskaPackets({ readRange, fileSize, portionBytes, l
|
|
|
120
130
|
at = element.end ?? cursor;
|
|
121
131
|
state.at = at;
|
|
122
132
|
state.cluster = null;
|
|
133
|
+
readableUntil = null;
|
|
123
134
|
}
|
|
124
135
|
if (at < layout.segmentEnd) return "until";
|
|
125
136
|
state.reachedEnd = true;
|