@torrent-tv/proxy 2.92.4 → 2.92.5
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
|
@@ -137,7 +137,7 @@ export class MatroskaContainer extends Container {
|
|
|
137
137
|
/** @type {Array<import("../tracks/ContainerTrack.js").ContainerTrack> | null} */
|
|
138
138
|
#tracks = null;
|
|
139
139
|
#packets = null;
|
|
140
|
-
#
|
|
140
|
+
#packetStates = new Map();
|
|
141
141
|
|
|
142
142
|
/**
|
|
143
143
|
* The Cues reading: undefined until read, null when the file has none.
|
|
@@ -150,8 +150,8 @@ export class MatroskaContainer extends Container {
|
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
packetIndexBytes() {
|
|
153
|
-
return
|
|
154
|
-
[...(
|
|
153
|
+
return [...this.#packetStates.values()].reduce((sum, state) => sum +
|
|
154
|
+
[...(state.packets?.values() ?? [])].reduce((bytes, records) => bytes + records.allocatedBytes, 0), 0);
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
static detect(head) {
|
|
@@ -437,16 +437,50 @@ export class MatroskaContainer extends Container {
|
|
|
437
437
|
if (!layout) throw new Error("Matroska Segment is absent.");
|
|
438
438
|
const tracks = await this.readTracks();
|
|
439
439
|
const info = await this.readMediaInfo();
|
|
440
|
+
const start = await this.#packetStart(layout, tracks, interval);
|
|
441
|
+
// Reuse a contiguous reading that has already reached this cue. A seek
|
|
442
|
+
// across an unindexed gap gets its own reading instead of walking the gap.
|
|
443
|
+
let state = [...this.#packetStates.values()]
|
|
444
|
+
.filter(one => one.startAt <= start && one.at >= start)
|
|
445
|
+
.sort((a, b) => b.startAt - a.startAt)[0];
|
|
446
|
+
if (!state) {
|
|
447
|
+
state = { startAt: start, at: start };
|
|
448
|
+
this.#packetStates.set(start, state);
|
|
449
|
+
}
|
|
440
450
|
const index = await readMatroskaPackets({ readRange: this.readRange,
|
|
441
451
|
fileSize: this.fileSize, portionBytes: this.portionBytes, layout, tracks,
|
|
442
|
-
durationSeconds: info.durationSeconds, interval, state
|
|
443
|
-
if (index.isComplete()) {
|
|
452
|
+
durationSeconds: info.durationSeconds, interval, state, packetMemory: this.packetMemory });
|
|
453
|
+
if (index.isComplete() && state.startAt === layout.firstClusterAt) {
|
|
444
454
|
this.#packets = index;
|
|
445
|
-
this.#packetState = {};
|
|
446
455
|
}
|
|
447
456
|
return index;
|
|
448
457
|
}
|
|
449
458
|
|
|
459
|
+
async #packetStart(layout, tracks, interval) {
|
|
460
|
+
const first = layout.firstClusterAt ?? layout.segmentEnd;
|
|
461
|
+
if (!Number.isFinite(interval?.from) || interval.from <= 0) return first;
|
|
462
|
+
const cues = await this.readCues();
|
|
463
|
+
const requested = tracks.filter(track => interval.trackIds ? interval.trackIds.includes(track.trackNumber)
|
|
464
|
+
: ["video", "audio"].includes(track.type));
|
|
465
|
+
// A subtitle can remain visible across arbitrarily many picture Clusters;
|
|
466
|
+
// picture cues alone cannot prove that an earlier subtitle has ended.
|
|
467
|
+
if (requested.every(track => track.type === "subtitle")) return first;
|
|
468
|
+
const cueTrack = requested.find(track => track.type === "video") ??
|
|
469
|
+
tracks.find(track => track.type === "video") ?? requested[0];
|
|
470
|
+
if (!cueTrack) return first;
|
|
471
|
+
const points = (cues?.points ?? []).flatMap(point => point.positions
|
|
472
|
+
.filter(position => position.track === cueTrack.trackNumber && position.clusterAt >= first && position.clusterAt < layout.segmentEnd)
|
|
473
|
+
.map(position => ({ at: position.clusterAt, seconds: point.ticks * layout.secondsPerTick })))
|
|
474
|
+
.sort((a, b) => a.seconds - b.seconds);
|
|
475
|
+
const preroll = Math.max(0, ...requested.map(track =>
|
|
476
|
+
Math.max(track.seekPrerollSeconds ?? 0, track.defaultDurationSeconds ?? 0)));
|
|
477
|
+
let preceding = -1;
|
|
478
|
+
for (let index = 0; index < points.length && points[index].seconds <= interval.from - preroll; index++) preceding = index;
|
|
479
|
+
// The preceding cue's Cluster also retains audio that overlaps this cut
|
|
480
|
+
// and decoder dependencies before the first presented packet.
|
|
481
|
+
return preceding < 0 ? first : points[Math.max(0, preceding - 1)].at;
|
|
482
|
+
}
|
|
483
|
+
|
|
450
484
|
/**
|
|
451
485
|
* The Cues table, read once, every CuePoint with every track it names.
|
|
452
486
|
*
|
|
@@ -15,7 +15,7 @@ export async function readMatroskaPackets({ readRange, fileSize, portionBytes, l
|
|
|
15
15
|
const declared = new Map(tracks.filter(track => ["video", "audio", "subtitle"].includes(track.type)).map(track => [track.trackNumber, track]));
|
|
16
16
|
if (!state.packets) {
|
|
17
17
|
state.packets = new Map([...declared.keys()].map(id => [id, new PacketRecords(packetMemory)]));
|
|
18
|
-
state.at
|
|
18
|
+
state.at ??= layout.firstClusterAt ?? layout.segmentEnd;
|
|
19
19
|
}
|
|
20
20
|
const packets = state.packets;
|
|
21
21
|
if (intervalCovered(declared, packets, interval)) return buildIndex(declared, packets, durationSeconds, false, interval, packetMemory);
|