@torrent-tv/proxy 2.104.2 → 2.105.1
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
|
@@ -155,7 +155,11 @@ export class ContainerOrchestrator {
|
|
|
155
155
|
// picture's first input of an AVI waited about 4 s behind the sound's
|
|
156
156
|
// table of the whole film it does not use (r38, torrent-tv/meta#178).
|
|
157
157
|
const aside = this.#answersWithoutReading(key, statement);
|
|
158
|
-
|
|
158
|
+
// Chapter discovery can walk a whole file when the format has no chapter
|
|
159
|
+
// index. Keep it serial with other chapter reads, but out of the queue used
|
|
160
|
+
// to prepare playback inputs for this file.
|
|
161
|
+
const queueKey = statement === "chapters" ? `${key}:chapters` : key;
|
|
162
|
+
const previous = aside ? Promise.resolve() : this.#reads.get(queueKey) ?? Promise.resolve();
|
|
159
163
|
const askedAt = performance.now();
|
|
160
164
|
const reading = previous.catch(() => undefined).then(async () => {
|
|
161
165
|
if (!isCurrent()) return { result: { kind: "terminal", reason: "request-obsolete", requestId: params.requestId } };
|
|
@@ -168,7 +172,7 @@ export class ContainerOrchestrator {
|
|
|
168
172
|
readMs: endedAt - startedAt, cached: aside, result: result.kind, reason: result.reason, ranges: result.ranges });
|
|
169
173
|
return { result, revision };
|
|
170
174
|
});
|
|
171
|
-
if (!aside) this.#reads.set(
|
|
175
|
+
if (!aside) this.#reads.set(queueKey, reading);
|
|
172
176
|
try {
|
|
173
177
|
const { result, revision } = await reading;
|
|
174
178
|
if (!isCurrent()) return { kind: "terminal", reason: "request-obsolete", requestId: params.requestId };
|
|
@@ -177,7 +181,7 @@ export class ContainerOrchestrator {
|
|
|
177
181
|
await params.onReadResult?.(statement, result, revision);
|
|
178
182
|
return result;
|
|
179
183
|
} finally {
|
|
180
|
-
if (this.#reads.get(
|
|
184
|
+
if (this.#reads.get(queueKey) === reading) this.#reads.delete(queueKey);
|
|
181
185
|
}
|
|
182
186
|
}
|
|
183
187
|
|
|
@@ -11,11 +11,12 @@ const TOP_LEVEL = new Set([SEEK_HEAD, CHAPTERS, CLUSTER, 0x1549a966, 0x1654ae6b,
|
|
|
11
11
|
async function header(reader, at, limit) {
|
|
12
12
|
reader.step();
|
|
13
13
|
const vint = async (position, marker) => {
|
|
14
|
-
const
|
|
14
|
+
const firstBytes = await reader.bytes(position, 1);
|
|
15
|
+
const first = firstBytes[0];
|
|
15
16
|
let n = 1;
|
|
16
17
|
while (n <= 8 && !(first & (0x80 >> (n - 1)))) n++;
|
|
17
18
|
if (n > (marker ? 4 : 8) || position + n > limit) throw new Error("Invalid chapter EBML header");
|
|
18
|
-
const bytes = await reader.bytes(position, n);
|
|
19
|
+
const bytes = n === 1 ? firstBytes : Buffer.concat([firstBytes, await reader.bytes(position + 1, n - 1)]);
|
|
19
20
|
const value = readVint(bytes, 0, marker);
|
|
20
21
|
const mask = 2 ** (8 - n) - 1;
|
|
21
22
|
return { ...value, unknown: !marker && (bytes[0] & mask) === mask && bytes.subarray(1).every((byte) => byte === 255) };
|
|
@@ -608,12 +608,30 @@ export function sidecarNaming({ folders, fileName, videoName = "" }) {
|
|
|
608
608
|
stripExtension(String(videoName ?? ""))
|
|
609
609
|
);
|
|
610
610
|
const language = fromName.language ?? languageFromFolderNames(folders);
|
|
611
|
+
const title = titleOf(fromName.title);
|
|
612
|
+
const team = teamFrom({ folders, fileName, videoName });
|
|
611
613
|
return {
|
|
612
614
|
code: language?.code ?? null,
|
|
613
615
|
name: language?.name ?? null,
|
|
614
|
-
releaser:
|
|
616
|
+
// Keep releaser unchanged: subtitle identity uses this value across episodes.
|
|
617
|
+
releaser: title ?? releaserFrom({ folders, fileName, videoName }),
|
|
618
|
+
team,
|
|
619
|
+
title,
|
|
615
620
|
isForced: fromName.isForced,
|
|
616
621
|
isHearingImpaired: fromName.isHearingImpaired,
|
|
617
622
|
isDefault: fromName.isDefault
|
|
618
623
|
};
|
|
619
624
|
}
|
|
625
|
+
|
|
626
|
+
/**
|
|
627
|
+
* A team is only a distinct bracketed name. Generic track titles are not teams.
|
|
628
|
+
* @param {{ folders: string[], fileName: string, videoName?: string }} params
|
|
629
|
+
* @returns {string | null}
|
|
630
|
+
*/
|
|
631
|
+
function teamFrom({ folders, fileName, videoName = "" }) {
|
|
632
|
+
const team = releaserFrom({ folders, fileName, videoName });
|
|
633
|
+
if (!team || /^(?:full|signs?|commentary)$/iu.test(team.trim())) {
|
|
634
|
+
return null;
|
|
635
|
+
}
|
|
636
|
+
return team;
|
|
637
|
+
}
|