@torrent-tv/proxy 2.94.1 → 2.94.2
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
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { logger } from "../../utils/logger.js";
|
|
11
11
|
import { isOutputName } from "../encode/output/index.js";
|
|
12
|
+
import { withSubtitleReadiness } from "./subtitle-readiness.js";
|
|
12
13
|
|
|
13
14
|
function isWarmupTimeoutError(error) {
|
|
14
15
|
if (!(error instanceof Error)) {
|
|
@@ -696,7 +697,13 @@ export class ViewerRequests {
|
|
|
696
697
|
tracks,
|
|
697
698
|
linkReadings: measurement.link.snapshot()
|
|
698
699
|
};
|
|
699
|
-
|
|
700
|
+
// A subtitle track chosen inside THIS file is the last input the forecast
|
|
701
|
+
// waits for. A subtitle FILE beside it is named by its own index and is
|
|
702
|
+
// fetched whole by the page before it is chosen, so it never matches here.
|
|
703
|
+
const subtitle = viewer?.subtitle?.sourceKey === session.file.sourceKey &&
|
|
704
|
+
viewer.subtitle.fileIndex === session.file.fileIndex ? viewer.subtitle : null;
|
|
705
|
+
const forecast = withSubtitleReadiness(this.#host.playbackReadiness.predict(input), subtitle,
|
|
706
|
+
subtitle ? this.#host.subtitleReadyFor?.(viewer) !== false : true);
|
|
700
707
|
forecast.operations = outputs.flatMap((output, index) => {
|
|
701
708
|
const common = { speed: this.#host.playbackReadiness.forecastRate(tracks[index].readings), processedSeconds: tracks[index].processedSeconds };
|
|
702
709
|
return [
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file The subtitle a viewer has chosen, as a term of their playback readiness.
|
|
3
|
+
*
|
|
4
|
+
* A file that says which subtitle to show — `FlagDefault` — is saying the film
|
|
5
|
+
* is to be watched with it, the opening included. Starting the picture before
|
|
6
|
+
* that track's cues have been read where the viewer stands showed the opening
|
|
7
|
+
* without them (torrent-tv/meta#8). So the chosen track is one more input the
|
|
8
|
+
* forecast waits for, beside the picture and the separately published sound.
|
|
9
|
+
*
|
|
10
|
+
* **What bounds the wait.** The read it waits for is one segment of that track
|
|
11
|
+
* at the viewer's position (`SourcePreparation`, the `subtitle-embedded`
|
|
12
|
+
* work), demanded at the picture's own urgency. It ends with the cues or with
|
|
13
|
+
* the proxy's refusal to read the track, and `subtitleReadyFor` counts both.
|
|
14
|
+
* No time limit is added: there is no measured figure to set one from, and the
|
|
15
|
+
* bytes it needs are the ones beside the picture's own at the same position.
|
|
16
|
+
*
|
|
17
|
+
* The answer always carries `subtitles`, `null` included, so the page can tell
|
|
18
|
+
* a proxy that weighed the chosen track from one that never heard of it.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @param {object} forecast - What `predictPlaybackReadiness` answered.
|
|
23
|
+
* @param {{ fileIndex: number, trackIndex: number | null } | null} selection -
|
|
24
|
+
* The subtitle this viewer chose, for the file being opened; null for none.
|
|
25
|
+
* @param {boolean} ready - Whether that subtitle has been read where they stand.
|
|
26
|
+
* @returns {object} The forecast, holding for the subtitle where it must.
|
|
27
|
+
*/
|
|
28
|
+
export function withSubtitleReadiness(forecast, selection, ready) {
|
|
29
|
+
const subtitles = selection
|
|
30
|
+
? { fileIndex: selection.fileIndex, trackIndex: selection.trackIndex, ready: ready === true }
|
|
31
|
+
: null;
|
|
32
|
+
if (forecast?.ready !== true || !subtitles || subtitles.ready) {
|
|
33
|
+
return { ...forecast, subtitles };
|
|
34
|
+
}
|
|
35
|
+
// No delay: the forecast's figure is the picture's, which is already met, and
|
|
36
|
+
// no rate predicts when a read of a few cues finishes.
|
|
37
|
+
return { ...forecast, ready: false, delaySeconds: null, reason: "subtitles-pending", subtitles };
|
|
38
|
+
}
|
|
@@ -572,6 +572,9 @@ export function wireOutputs({
|
|
|
572
572
|
// When this viewer's playback can start and run to the end: the viewer
|
|
573
573
|
// component's forecast, which models the viewer's browser.
|
|
574
574
|
playbackReadiness: { predict: predictPlaybackReadiness, forecastRate, RateTrend },
|
|
575
|
+
// Whether the subtitle a viewer chose has been read where they stand — the
|
|
576
|
+
// source preparation's answer, set on these parts by the assembly.
|
|
577
|
+
subtitleReadyFor: (viewer) => parts.subtitleReadyFor?.(viewer) ?? true,
|
|
575
578
|
sourceInputsFor: (output, index) => parts.sourceInputsFor?.(output, index),
|
|
576
579
|
encodeSpeedReadingOf: (output) => parts.encodeCost.latestSpeedReadingOf(output),
|
|
577
580
|
projectedEncodeSpeedOf: (output) => parts.encodeCost.projectedSpeedOf(output),
|
|
@@ -104,14 +104,26 @@ export class SourcePreparation {
|
|
|
104
104
|
work.positions === this.#positionsFor(work.sourceKey, work.fileIndex, viewer.id));
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
/**
|
|
108
|
+
* Whether the subtitle this viewer chose has been read where they stand.
|
|
109
|
+
*
|
|
110
|
+
* Playback readiness waits on this (`subtitle-readiness.js`), so every way
|
|
111
|
+
* the read can END must answer it, and a refusal is one of them. The cues
|
|
112
|
+
* are the last of four reads — the track table, the media info, the packet
|
|
113
|
+
* index, then the cues — and a refusal at an earlier step means the later
|
|
114
|
+
* ones are never asked. Counting only the cues' own answer left a track the
|
|
115
|
+
* proxy cannot read waiting for ever.
|
|
116
|
+
*/
|
|
107
117
|
subtitleReadyFor(viewer) {
|
|
108
118
|
const selection = viewer.subtitle;
|
|
109
119
|
if (!selection) return true;
|
|
120
|
+
const positions = () => this.#positionsFor(selection.sourceKey, selection.fileIndex, viewer.id, true);
|
|
110
121
|
return [...this.#work.values()].some(work => work.sourceKey === selection.sourceKey &&
|
|
111
122
|
work.fileIndex === selection.fileIndex && ["result", "terminal"].includes(work.result) &&
|
|
112
123
|
(selection.trackIndex === null ? work.statement === "subtitle-file" && work.ownerFileIndex === viewer.source.selectedFileIndex
|
|
113
|
-
: work.
|
|
114
|
-
work.positions ===
|
|
124
|
+
: work.role === "subtitle-embedded" && work.ownerId === viewer.id && work.trackIndex === selection.trackIndex &&
|
|
125
|
+
(work.positions === undefined || work.positions === positions()) &&
|
|
126
|
+
(work.statement === "subtitle-cues" || work.result === "terminal")));
|
|
115
127
|
}
|
|
116
128
|
|
|
117
129
|
async bytesChanged(sourceKey, fileIndex) {
|