@torrent-tv/proxy 2.101.33 → 2.101.35
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
|
@@ -290,10 +290,13 @@ export class LocalObservations {
|
|
|
290
290
|
}
|
|
291
291
|
|
|
292
292
|
/**
|
|
293
|
-
* Price the early wait from separate measured stage medians
|
|
294
|
-
*
|
|
295
|
-
*
|
|
296
|
-
*
|
|
293
|
+
* Price the early wait from separate measured stage medians on this proxy
|
|
294
|
+
* configuration. Before the container and track operations are known, use
|
|
295
|
+
* measurements from every observed file type; once current cache and
|
|
296
|
+
* operation facts are known, retain only observations that match those
|
|
297
|
+
* facts. Numeric proximity to a past file, source rate, or link rate never
|
|
298
|
+
* selects a completed run. Each observation contributes once, using its
|
|
299
|
+
* latest snapshot that states the current cache and operation path.
|
|
297
300
|
*
|
|
298
301
|
* @param {object} features
|
|
299
302
|
* @returns {number | null}
|
|
@@ -303,7 +306,7 @@ export class LocalObservations {
|
|
|
303
306
|
const stages = ["transportMs", "planMs", "prepareMs", "prebufferMs", "presentationMs"];
|
|
304
307
|
const candidates = [];
|
|
305
308
|
for (const reading of this.#entries.firstFrame[this.#firstFrameKey()] ?? []) {
|
|
306
|
-
if (
|
|
309
|
+
if (!reading.stages) continue;
|
|
307
310
|
const points = Array.isArray(reading.featureSnapshots) && reading.featureSnapshots.length > 0
|
|
308
311
|
? reading.featureSnapshots : [reading];
|
|
309
312
|
const point = [...points].reverse().find(candidate => firstFramePathMatches(features, candidate));
|
|
@@ -1031,48 +1031,6 @@ export class SegmentServing {
|
|
|
1031
1031
|
}
|
|
1032
1032
|
}
|
|
1033
1033
|
|
|
1034
|
-
/**
|
|
1035
|
-
* Wait for a valid HLS playlist or a terminal output state, without polling.
|
|
1036
|
-
*
|
|
1037
|
-
* @param {HlsSession} session
|
|
1038
|
-
* @returns {Promise<void>}
|
|
1039
|
-
*/
|
|
1040
|
-
async waitUntilReady(session) {
|
|
1041
|
-
// With a synthetic VOD playlist there is nothing to wait for: the playlist
|
|
1042
|
-
// is generated from the probed duration and is available immediately.
|
|
1043
|
-
// Individual segments are long-polled by the segment route as ffmpeg
|
|
1044
|
-
// produces them.
|
|
1045
|
-
if (session.useSyntheticPlaylist) {
|
|
1046
|
-
if (this.#host.encodeRuns.hasFailed(session)) {
|
|
1047
|
-
throw new Error(this.#host.encodeRuns.lastErrorOf(session) || "ffmpeg failed to start HLS session.");
|
|
1048
|
-
}
|
|
1049
|
-
return;
|
|
1050
|
-
}
|
|
1051
|
-
|
|
1052
|
-
const playlistPath = path.join(this.#host.segmentStore.pathFor(session.outputKey ?? ""), PLAYLIST_FILE_NAME);
|
|
1053
|
-
while (this.#host.outputs.get(session.id) === session) {
|
|
1054
|
-
const change = this.subscribeFileChange(session.id);
|
|
1055
|
-
try {
|
|
1056
|
-
if (this.#host.encodeRuns.hasFailed(session)) {
|
|
1057
|
-
throw new Error(this.#host.encodeRuns.lastErrorOf(session) || "ffmpeg failed to start HLS session.");
|
|
1058
|
-
}
|
|
1059
|
-
try {
|
|
1060
|
-
await access(playlistPath);
|
|
1061
|
-
const text = await readFile(playlistPath, "utf8");
|
|
1062
|
-
if (text.includes("#EXTM3U")) {
|
|
1063
|
-
return;
|
|
1064
|
-
}
|
|
1065
|
-
} catch (_error) {
|
|
1066
|
-
// Playlist is not ready yet.
|
|
1067
|
-
}
|
|
1068
|
-
await change.changed;
|
|
1069
|
-
} finally {
|
|
1070
|
-
change.release();
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
throw new Error("HLS output was disposed before its playlist became available.");
|
|
1074
|
-
}
|
|
1075
|
-
|
|
1076
1034
|
/**
|
|
1077
1035
|
* Answer the player's report that a delivered fragment sits far from the edge
|
|
1078
1036
|
* of its buffer, with the one fact only this side holds: which boundary the
|
|
@@ -11,13 +11,6 @@ import { logger } from "../../utils/logger.js";
|
|
|
11
11
|
import { isOutputName, OutputSpec } from "../encode/output/index.js";
|
|
12
12
|
import { withSubtitleReadiness } from "./subtitle-readiness.js";
|
|
13
13
|
|
|
14
|
-
function isWarmupTimeoutError(error) {
|
|
15
|
-
if (!(error instanceof Error)) {
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
18
|
-
return error.message === "HLS playlist is still warming up.";
|
|
19
|
-
}
|
|
20
|
-
|
|
21
14
|
/**
|
|
22
15
|
* The segment number an assignment is recorded under for this file, −1 for the
|
|
23
16
|
* init, or null for anything that is neither (a playlist).
|
|
@@ -93,7 +86,7 @@ export class ViewerRequests {
|
|
|
93
86
|
#forecastPieces = new WeakMap();
|
|
94
87
|
|
|
95
88
|
/**
|
|
96
|
-
* @param {object} host - `opening`, `activeOutputFor`, `viewerSecondsOn`, `minimumBufferSecondsFor`, `getSourceStats`, `
|
|
89
|
+
* @param {object} host - `opening`, `activeOutputFor`, `viewerSecondsOn`, `minimumBufferSecondsFor`, `getSourceStats`, `planEncodersSoon`, `encodeRuns`, `encodeSpeedReadingsOf`, `outputTimes`, `outputs`, `lookaheadSeconds`, `quality`, `qualityOffer`, `renditions`, `segmentDurationSec`, `segmentStore`, `sourceFiles`, `startupWaitMs`, `viewers`, `acceptsGeneration`, `generationOfRequest`, `noteGivenOutput`, `holdForResponse`, `noteServingVerdict`, `servingVerdictOf`
|
|
97
90
|
*/
|
|
98
91
|
constructor(host) {
|
|
99
92
|
this.#host = host;
|
|
@@ -421,19 +414,13 @@ export class ViewerRequests {
|
|
|
421
414
|
// Where the first encoder goes is the plan's, from where the viewers stand.
|
|
422
415
|
this.#host.planEncodersSoon();
|
|
423
416
|
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
if (existed && !isWarmupTimeoutError(error)) {
|
|
432
|
-
throw error;
|
|
433
|
-
}
|
|
434
|
-
// Not a failure of the request: the synthetic playlist is already there
|
|
435
|
-
// and segments appear as the encoder produces them.
|
|
436
|
-
}
|
|
417
|
+
// Return the allocated session before a keyframe-based output writes its
|
|
418
|
+
// first playlist. The browser can then poll this session's live playback
|
|
419
|
+
// forecast while the source, proxy and client-link work is still underway;
|
|
420
|
+
// otherwise this request hides all revised estimates until the first
|
|
421
|
+
// segment is already ready. The playlist route and the viewer's existing
|
|
422
|
+
// wait loop handle output that is still being prepared, and the progress
|
|
423
|
+
// route reports a terminal encoder failure.
|
|
437
424
|
return output;
|
|
438
425
|
}
|
|
439
426
|
|
|
@@ -631,7 +631,6 @@ export function wireOutputs({
|
|
|
631
631
|
getSourceStats: (...args) => parts.getSourceStats?.(...args) ?? null,
|
|
632
632
|
disposeSession: (...args) => parts.lifecycle.disposeSession(...args),
|
|
633
633
|
planEncodersSoon: (...args) => parts.encodeRuns.planEncodersSoon(...args),
|
|
634
|
-
waitUntilReady: (...args) => parts.serving.waitUntilReady(...args),
|
|
635
634
|
noteViewerWaiting: (viewer, wait) => parts.encodeOrchestrator?.noteViewerWaiting(viewer, wait),
|
|
636
635
|
get encodeRuns() { return parts.encodeRuns; },
|
|
637
636
|
// When this viewer's playback can start and run to the end: the viewer
|