@torrent-tv/proxy 2.96.6 → 2.96.7
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
|
@@ -495,9 +495,17 @@ export async function startProxyServer({
|
|
|
495
495
|
const known = segmentSourceRanges.get(rangesKey);
|
|
496
496
|
if (known) { convert(known); continue; }
|
|
497
497
|
if (direct) {
|
|
498
|
+
// Asked per zone too: a soundtrack of an AVI is read from its own
|
|
499
|
+
// packets, which the same index states (torrent-tv/meta#166).
|
|
498
500
|
let value = null;
|
|
499
|
-
try {
|
|
500
|
-
|
|
501
|
+
try {
|
|
502
|
+
if (await direct.supportsOriginalSourceRanges(interval) === true) value = await direct.readSourceRanges(interval);
|
|
503
|
+
else {
|
|
504
|
+
const index = await direct.readPacketIndex(interval);
|
|
505
|
+
await index.prepareAudioDependencies?.(interval, direct.readRange);
|
|
506
|
+
value = new SegmentInputs({ index, tracks: wanted }).forInterval({ ...interval, mode: track => modes.get(track) });
|
|
507
|
+
}
|
|
508
|
+
} catch { value = null; }
|
|
501
509
|
if (!map.isCurrent()) return [];
|
|
502
510
|
if (value?.kind === "result") {
|
|
503
511
|
segmentSourceRanges.set(rangesKey, value.ranges);
|
|
@@ -198,7 +198,7 @@ export class ContainerOrchestrator {
|
|
|
198
198
|
value = await (statement === "cover" ? container.readCover(edgesOf(params)) : container.readWorkTags(edgesOf(params)));
|
|
199
199
|
if (value === null) return { kind: "terminal", reason: statement === "cover" ? "no-cover" : "format-states-nothing", requestId };
|
|
200
200
|
} else if (statement === "source-navigation") {
|
|
201
|
-
value = await container.supportsOriginalSourceRanges?.() === true;
|
|
201
|
+
value = await container.supportsOriginalSourceRanges?.(params.packetInterval ?? null) === true;
|
|
202
202
|
} else if (statement === "source-ranges") {
|
|
203
203
|
if (typeof container.readSourceRanges !== "function") return { kind: "terminal", reason: "source-range-index-not-supported", requestId };
|
|
204
204
|
value = await container.readSourceRanges(params.packetInterval);
|
|
@@ -32,6 +32,8 @@ export class AviContainer extends Container {
|
|
|
32
32
|
#headers = null;
|
|
33
33
|
#tracks = null;
|
|
34
34
|
#packets = null;
|
|
35
|
+
/** The sound packets of an indexed file while they are being stated, so two asks build them once. */
|
|
36
|
+
#soundPacketsReading = null;
|
|
35
37
|
#scan = null;
|
|
36
38
|
#declarations = null;
|
|
37
39
|
#workTags = null;
|
|
@@ -150,10 +152,18 @@ export class AviContainer extends Container {
|
|
|
150
152
|
return index;
|
|
151
153
|
}
|
|
152
154
|
|
|
153
|
-
|
|
155
|
+
/**
|
|
156
|
+
* Whether `interval` is read from the original file. Only a picture needs
|
|
157
|
+
* it: an AVI states no presentation time, so FFmpeg's own reading is what
|
|
158
|
+
* keeps its frames in order. Sound alone is read from its own packets, whose
|
|
159
|
+
* time the index states (torrent-tv/meta#166); asked of the file as a whole,
|
|
160
|
+
* the answer is the picture's.
|
|
161
|
+
*/
|
|
162
|
+
async supportsOriginalSourceRanges(interval = null) {
|
|
154
163
|
const index = await this.#readIndex();
|
|
155
164
|
if (!index) return false;
|
|
156
165
|
const picture = ContainerTrack.firstUsable(await this.readTracks(), "video");
|
|
166
|
+
if (picture && interval?.trackIds?.length && !interval.trackIds.includes(picture.trackNumber)) return false;
|
|
157
167
|
return !picture || index.keyframeTimes(picture.trackNumber).length > 0;
|
|
158
168
|
}
|
|
159
169
|
|
|
@@ -312,9 +322,19 @@ export class AviContainer extends Container {
|
|
|
312
322
|
async readPacketIndex(interval) {
|
|
313
323
|
if (this.#packets) return this.#packets;
|
|
314
324
|
if (this.#scan) return this.#readUnindexed(interval);
|
|
315
|
-
// A file with an index is read by FFmpeg from the original
|
|
316
|
-
//
|
|
317
|
-
|
|
325
|
+
// A picture of a file with an index is read by FFmpeg from the original
|
|
326
|
+
// bytes (torrent-tv/meta#151). Its SOUND is not: the index states every
|
|
327
|
+
// sound packet's place and FFmpeg's time for it, and a soundtrack read from
|
|
328
|
+
// the original file has to carry the picture's bytes too, because sound and
|
|
329
|
+
// picture are interleaved — field 2026-10-10, a two-hour film's soundtrack
|
|
330
|
+
// copied 2.3 GB for 0.19 GB of its own (torrent-tv/meta#166).
|
|
331
|
+
const indexed = await this.#readIndex();
|
|
332
|
+
if (indexed) {
|
|
333
|
+
this.#soundPacketsReading ??= this.#soundPacketsOf(indexed).then(
|
|
334
|
+
packets => (this.#packets = packets),
|
|
335
|
+
error => { this.#soundPacketsReading = null; throw error; });
|
|
336
|
+
return this.#soundPacketsReading;
|
|
337
|
+
}
|
|
318
338
|
const tracks = await this.readTracks();
|
|
319
339
|
const { streams } = await this.#readHeaders();
|
|
320
340
|
const index = new PacketIndex({ packetMemory: this.packetMemory, deferMemory: true });
|
|
@@ -333,6 +353,45 @@ export class AviContainer extends Container {
|
|
|
333
353
|
return this.#readUnindexed(interval);
|
|
334
354
|
}
|
|
335
355
|
|
|
356
|
+
/**
|
|
357
|
+
* The sound packets an AVI index states, on FFmpeg's clock: the time the
|
|
358
|
+
* index gives each packet is the one FFmpeg's demuxer gives it, and a packet
|
|
359
|
+
* lasts until the next one of its stream, the last until the stream's clock
|
|
360
|
+
* after it. Only sound is declared; a picture of an indexed AVI states no
|
|
361
|
+
* presentation time and is read from the original file.
|
|
362
|
+
*
|
|
363
|
+
* @param {{ packets: Array<{ count: number, starts: Float64Array, lengths: Uint32Array, times: Float64Array, end: number | null }> }} index
|
|
364
|
+
* @returns {Promise<PacketIndex>}
|
|
365
|
+
*/
|
|
366
|
+
async #soundPacketsOf(index) {
|
|
367
|
+
const tracks = await this.readTracks();
|
|
368
|
+
const { streams } = await this.#readHeaders();
|
|
369
|
+
const packets = new PacketIndex({ packetMemory: this.packetMemory });
|
|
370
|
+
try {
|
|
371
|
+
for (const track of tracks.filter(one => one.type === "audio")) {
|
|
372
|
+
const stream = index.packets[track.trackNumber];
|
|
373
|
+
packets.declareTrack(track.trackNumber, { type: "audio", codecId: track.codecId,
|
|
374
|
+
codecRanges: streams[track.trackNumber].codecRanges });
|
|
375
|
+
let previous = -1;
|
|
376
|
+
const append = (end) => {
|
|
377
|
+
packets.append(track.trackNumber, { pts: stream.times[previous], duration: Math.max(0, end - stream.times[previous]),
|
|
378
|
+
keyframe: true, ranges: [[stream.starts[previous], stream.starts[previous] + stream.lengths[previous] - 1]] });
|
|
379
|
+
};
|
|
380
|
+
for (let at = 0; at < stream.count; at++) {
|
|
381
|
+
if (!stream.lengths[at]) continue;
|
|
382
|
+
if (previous >= 0) append(stream.times[at]);
|
|
383
|
+
previous = at;
|
|
384
|
+
}
|
|
385
|
+
if (previous >= 0) append(stream.end ?? stream.times[previous]);
|
|
386
|
+
packets.complete(track.trackNumber);
|
|
387
|
+
}
|
|
388
|
+
} catch (error) {
|
|
389
|
+
packets.dispose();
|
|
390
|
+
throw error;
|
|
391
|
+
}
|
|
392
|
+
return packets;
|
|
393
|
+
}
|
|
394
|
+
|
|
336
395
|
async #readUnindexed(interval) {
|
|
337
396
|
const state = this.#scan;
|
|
338
397
|
state.index.flushPending();
|
|
@@ -44,6 +44,8 @@ class StreamPackets {
|
|
|
44
44
|
this.lengths = new Uint32Array(count);
|
|
45
45
|
this.times = new Float64Array(count);
|
|
46
46
|
this.keyframes = new Uint8Array(count);
|
|
47
|
+
/** Where the stream's clock stands after its last packet: the last packet's end. */
|
|
48
|
+
this.end = null;
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
push(start, length, time, keyframe) {
|
|
@@ -186,6 +188,7 @@ async function readIdx1({ read, fileSize, movi, idx1, streams, allocation }) {
|
|
|
186
188
|
clocks[id].advance(length);
|
|
187
189
|
}
|
|
188
190
|
}
|
|
191
|
+
packets.forEach((stream, id) => { stream.end = clocks[id].seconds(); });
|
|
189
192
|
return new AviIndex({ packets, indexRanges: [[idx1.start - 8, fileSize - 1]], held, allocation });
|
|
190
193
|
} catch (error) {
|
|
191
194
|
allocation?.release?.(held);
|
|
@@ -247,6 +250,7 @@ async function readOpenDml({ read, fileSize, streams, allocation }) {
|
|
|
247
250
|
}
|
|
248
251
|
}
|
|
249
252
|
}
|
|
253
|
+
packets.forEach((stream, id) => { stream.end = clocks[id].seconds(); });
|
|
250
254
|
return new AviIndex({ packets, indexRanges: parsed.map(standard => [standard.start, standard.end - 1]), held, allocation });
|
|
251
255
|
} catch (error) {
|
|
252
256
|
allocation?.release?.(held);
|