@torrent-tv/proxy 2.99.20 → 2.99.21
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
package/server.js
CHANGED
|
@@ -868,7 +868,7 @@ export async function startProxyServer({
|
|
|
868
868
|
},
|
|
869
869
|
// `timing`, when given, is told how long each read of a file waited behind
|
|
870
870
|
// the reads asked before it and how long it took (torrent-tv/meta#166).
|
|
871
|
-
resolveEncodeInput: async (output, fromIndex, toIndex, timing = null) => {
|
|
871
|
+
resolveEncodeInput: async (output, fromIndex, toIndex, timing = null, isCurrent = null) => {
|
|
872
872
|
const grid = output.timeline?.published ?? output.timeline?.boundaries;
|
|
873
873
|
const from = grid?.[fromIndex], to = grid?.[toIndex + 1];
|
|
874
874
|
if (!Number.isFinite(from) || !(to > from)) return { kind: "terminal", reason: "output-interval-not-declared" };
|
|
@@ -914,6 +914,7 @@ export async function startProxyServer({
|
|
|
914
914
|
if (media.kind !== "result") return media;
|
|
915
915
|
const timeShiftSeconds = Number(media.value?.startTimeSeconds) || 0;
|
|
916
916
|
const sourceInterval = output.timeline.sourceInterval(fromIndex, toIndex, timeShiftSeconds);
|
|
917
|
+
if (isCurrent) params.isCurrent = isCurrent;
|
|
917
918
|
params.packetInterval = { ...sourceInterval,
|
|
918
919
|
trackIds: wanted.map(track => track.trackNumber),
|
|
919
920
|
modes: Object.fromEntries(wanted.map(track => [track.trackNumber, modes.get(track)])) };
|
|
@@ -92,10 +92,10 @@ export class EncodeInputs {
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
/** The container's statement of an interval, its time added to the request's account. */
|
|
95
|
-
async #resolveTimed(timing, output, from, to) {
|
|
95
|
+
async #resolveTimed(timing, output, from, to, isCurrent = null) {
|
|
96
96
|
const startedAt = this.#now();
|
|
97
97
|
try {
|
|
98
|
-
return await this.#resolve(output, from, to, timing);
|
|
98
|
+
return await this.#resolve(output, from, to, timing, isCurrent);
|
|
99
99
|
} finally {
|
|
100
100
|
timing.resolutions += 1;
|
|
101
101
|
timing.resolveMs += this.#now() - startedAt;
|
|
@@ -384,7 +384,11 @@ export class EncodeInputs {
|
|
|
384
384
|
reads: 0, queuedMs: 0, readMs: 0, heldMs: 0, budgetMs: 0 };
|
|
385
385
|
request.timing.attempts += 1;
|
|
386
386
|
request.promise = (async () => {
|
|
387
|
-
|
|
387
|
+
// A request the plan withdraws stops its reads: a soundtrack piece's
|
|
388
|
+
// packet walk, left to run, held the file's reads and the piece the
|
|
389
|
+
// viewer had seeked to waited 16.9 s behind it (r59, torrent-tv/meta#178).
|
|
390
|
+
const resolved = await this.#resolveTimed(request.timing, request.output, request.from, request.from,
|
|
391
|
+
() => this.#requests.get(request.key) === request);
|
|
388
392
|
if (resolved.kind === "result") this.#original.set(request.output, resolved.sources?.every(source => source.input.original === true) === true);
|
|
389
393
|
if (this.#requests.get(request.key) !== request) return;
|
|
390
394
|
let result;
|
|
@@ -215,7 +215,8 @@ export class ContainerOrchestrator {
|
|
|
215
215
|
value = await container.readSourceRanges(params.packetInterval);
|
|
216
216
|
if (value.kind !== "result") return { ...value, requestId };
|
|
217
217
|
} else if (statement === "packets") {
|
|
218
|
-
value = await container.readPacketIndex(params.packetInterval);
|
|
218
|
+
value = await container.readPacketIndex(params.packetInterval, { isCurrent: params.isCurrent });
|
|
219
|
+
if (value === null) return { kind: "terminal", reason: "request-obsolete", requestId };
|
|
219
220
|
await value?.prepareAudioDependencies?.(params.packetInterval, container.readRange);
|
|
220
221
|
} else {
|
|
221
222
|
throw new TypeError(`Unknown media statement: ${statement}`);
|
|
@@ -782,7 +782,12 @@ export class MatroskaContainer extends Container {
|
|
|
782
782
|
streamSearchSeconds: FFMPEG_STREAM_SEARCH_SECONDS };
|
|
783
783
|
}
|
|
784
784
|
|
|
785
|
-
|
|
785
|
+
/**
|
|
786
|
+
* @param {object} [interval]
|
|
787
|
+
* @param {{ isCurrent?: () => boolean }} [options] - A walk whose request is
|
|
788
|
+
* no longer current stops between clusters and answers null.
|
|
789
|
+
*/
|
|
790
|
+
async readPacketIndex(interval, { isCurrent } = {}) {
|
|
786
791
|
if (this.#packets) return this.#packets;
|
|
787
792
|
const layout = await this.#segmentLayout();
|
|
788
793
|
if (!layout) throw new Error("Matroska Segment is absent.");
|
|
@@ -801,7 +806,8 @@ export class MatroskaContainer extends Container {
|
|
|
801
806
|
const complete = new Set();
|
|
802
807
|
let whole = true;
|
|
803
808
|
for (const track of requested) {
|
|
804
|
-
const chain = await this.#readTrackPackets(layout, tracks, track, start, interval);
|
|
809
|
+
const chain = await this.#readTrackPackets(layout, tracks, track, start, interval, isCurrent);
|
|
810
|
+
if (!chain) return null;
|
|
805
811
|
streams.set(track.trackNumber, chain.records);
|
|
806
812
|
if (chain.reachesEnd) complete.add(track.trackNumber);
|
|
807
813
|
if (!chain.reachesEnd || chain.startAt !== layout.firstClusterAt) whole = false;
|
|
@@ -825,7 +831,7 @@ export class MatroskaContainer extends Container {
|
|
|
825
831
|
*
|
|
826
832
|
* @returns {Promise<{ records: PacketRecordsChain, startAt: number, reachesEnd: boolean }>}
|
|
827
833
|
*/
|
|
828
|
-
async #readTrackPackets(layout, tracks, track, start, interval) {
|
|
834
|
+
async #readTrackPackets(layout, tracks, track, start, interval, isCurrent) {
|
|
829
835
|
const id = track.trackNumber;
|
|
830
836
|
const previous = this.#packetWalks.get(id) ?? Promise.resolve();
|
|
831
837
|
let finished;
|
|
@@ -882,7 +888,8 @@ export class MatroskaContainer extends Container {
|
|
|
882
888
|
const following = readings.find(reading => reading.startAt > current.startAt);
|
|
883
889
|
const outcome = await walkMatroskaPackets({ readRange: this.readRange, fileSize: this.fileSize,
|
|
884
890
|
portionBytes: this.portionBytes, layout, tracks, trackIds: [id], interval, packetMemory: this.packetMemory,
|
|
885
|
-
state: current, until: following?.startAt ?? null, isCovered: covered, readsThrough });
|
|
891
|
+
state: current, until: following?.startAt ?? null, isCovered: covered, readsThrough, isCurrent });
|
|
892
|
+
if (outcome === "obsolete") return null;
|
|
886
893
|
if (outcome !== "until") break;
|
|
887
894
|
current.next = following;
|
|
888
895
|
}
|
|
@@ -44,9 +44,12 @@ function recordedTracks(tracks, trackIds) {
|
|
|
44
44
|
* `start` on, where the caller knows it: those are read a portion at a time
|
|
45
45
|
* across clusters too.
|
|
46
46
|
*
|
|
47
|
-
*
|
|
47
|
+
* A walk whose `isCurrent` turns false stops at the next cluster, where its
|
|
48
|
+
* state is whole, and answers "obsolete".
|
|
49
|
+
*
|
|
50
|
+
* @returns {Promise<"covered" | "until" | "end" | "obsolete">}
|
|
48
51
|
*/
|
|
49
|
-
export async function walkMatroskaPackets({ readRange, fileSize, portionBytes, layout, tracks, trackIds, interval, packetMemory, state, until = null, isCovered = null, readsThrough = null }) {
|
|
52
|
+
export async function walkMatroskaPackets({ readRange, fileSize, portionBytes, layout, tracks, trackIds, interval, packetMemory, state, until = null, isCovered = null, readsThrough = null, isCurrent = null }) {
|
|
50
53
|
// INSIDE A CLUSTER EVERY BLOCK HEADER IS READ, so its bytes are read a
|
|
51
54
|
// portion at a time up to the cluster's end, and never past it. Read a
|
|
52
55
|
// header at a time, each a read of a few bytes from the torrent thread, the
|
|
@@ -71,6 +74,7 @@ export async function walkMatroskaPackets({ readRange, fileSize, portionBytes, l
|
|
|
71
74
|
if (covered()) return "covered";
|
|
72
75
|
let at = state.at;
|
|
73
76
|
while (at < limit) {
|
|
77
|
+
if (!state.cluster && isCurrent?.() === false) return "obsolete";
|
|
74
78
|
const element = state.cluster?.element ?? await validHeader(reader, at, layout.segmentEnd);
|
|
75
79
|
if (element.id !== CLUSTER) {
|
|
76
80
|
if (element.end === null) throw new Error("An unknown-sized Matroska element is not a Cluster.");
|