@torrent-tv/proxy 2.107.20 → 2.107.22
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
|
@@ -187,9 +187,12 @@ export class EncodeRun {
|
|
|
187
187
|
/** The film made and the run's own working time, as of the last progress report. */
|
|
188
188
|
#workSample = null;
|
|
189
189
|
|
|
190
|
-
/**
|
|
190
|
+
/** Baseline work sample for this run's next speed reading. */
|
|
191
191
|
#pieceSample = null;
|
|
192
192
|
|
|
193
|
+
/** A closed, published piece is waiting for a usable progress sample. */
|
|
194
|
+
#speedMeasurementPending = false;
|
|
195
|
+
|
|
193
196
|
/** @type {{ speed: number, at: number } | null} */
|
|
194
197
|
#speedReading = null;
|
|
195
198
|
|
|
@@ -302,6 +305,7 @@ export class EncodeRun {
|
|
|
302
305
|
this.progress = new RunProgress({ startSeconds, totalSeconds, now: this.now });
|
|
303
306
|
/** Where this run's time went: its own work, or waiting, or stopped. */
|
|
304
307
|
this.clock = new RunClock({ now: this.now });
|
|
308
|
+
this.#pieceSample = { at: this.now(), producedSeconds: 0, workingMs: 0 };
|
|
305
309
|
this.onEnded = typeof onEnded === "function" ? onEnded : () => {};
|
|
306
310
|
this.onProgress = typeof onProgress === "function" ? onProgress : () => {};
|
|
307
311
|
this.onProduced = typeof onProduced === "function" ? onProduced : () => {};
|
|
@@ -338,9 +342,9 @@ export class EncodeRun {
|
|
|
338
342
|
}
|
|
339
343
|
|
|
340
344
|
/**
|
|
341
|
-
* This run's processing speed: film made over its own working time
|
|
342
|
-
*
|
|
343
|
-
*
|
|
345
|
+
* This run's processing speed: film made over its own working time since the
|
|
346
|
+
* last baseline, with the time its input waited and the time it was stopped
|
|
347
|
+
* taken out (`RunClock`). Zero until a closed piece has enough progress data.
|
|
344
348
|
*
|
|
345
349
|
* @returns {number}
|
|
346
350
|
*/
|
|
@@ -349,8 +353,8 @@ export class EncodeRun {
|
|
|
349
353
|
}
|
|
350
354
|
|
|
351
355
|
/**
|
|
352
|
-
* The same speed with the moment it was measured, or null before a
|
|
353
|
-
* piece has
|
|
356
|
+
* The same speed with the moment it was measured, or null before a closed
|
|
357
|
+
* piece has enough progress data.
|
|
354
358
|
*
|
|
355
359
|
* @returns {{ speed: number, at: number } | null}
|
|
356
360
|
*/
|
|
@@ -452,6 +456,16 @@ export class EncodeRun {
|
|
|
452
456
|
/** An input read of this run has its bytes. */
|
|
453
457
|
inputWaitEnds() {
|
|
454
458
|
this.clock.inputWaitEnds();
|
|
459
|
+
if (this.#speedReading === null && this.#workSample !== null) {
|
|
460
|
+
// Progress made while an input read was waiting belongs to the wait, not
|
|
461
|
+
// to the encoder's rate. Start the first measurement from the latest
|
|
462
|
+
// progress known when the input resumes.
|
|
463
|
+
this.#pieceSample = {
|
|
464
|
+
...this.#workSample,
|
|
465
|
+
at: this.now(),
|
|
466
|
+
workingMs: this.clock.workingMs()
|
|
467
|
+
};
|
|
468
|
+
}
|
|
455
469
|
}
|
|
456
470
|
|
|
457
471
|
/** @returns {boolean} Whether an input read of this run is waiting for bytes now. */
|
|
@@ -589,6 +603,7 @@ export class EncodeRun {
|
|
|
589
603
|
producedSeconds: Math.max(0, this.progress.processedSeconds - this.progress.startPositionSeconds),
|
|
590
604
|
workingMs: this.clock.workingMs(at)
|
|
591
605
|
};
|
|
606
|
+
if (this.#speedMeasurementPending) this.#measureSpeed();
|
|
592
607
|
}
|
|
593
608
|
this.onProgress(this.progress.snapshot());
|
|
594
609
|
}
|
|
@@ -682,7 +697,6 @@ export class EncodeRun {
|
|
|
682
697
|
}
|
|
683
698
|
|
|
684
699
|
#publishClosed(name, following = null, { quiet = false, tentative = false } = {}) {
|
|
685
|
-
if (!tentative) this.#measureSpeed();
|
|
686
700
|
// ITS SERVED NAME, which is what whoever owns the disk gives it in answer.
|
|
687
701
|
// ffmpeg writes a piece under a working name and reports that; the piece
|
|
688
702
|
// becomes servable by being renamed, and everything below works in the
|
|
@@ -718,7 +732,8 @@ export class EncodeRun {
|
|
|
718
732
|
}
|
|
719
733
|
|
|
720
734
|
if (!served) return false;
|
|
721
|
-
|
|
735
|
+
this.#speedMeasurementPending = true;
|
|
736
|
+
this.#measureSpeed();
|
|
722
737
|
if (!this.#stopping) {
|
|
723
738
|
this.#provenName = served;
|
|
724
739
|
}
|
|
@@ -738,26 +753,22 @@ export class EncodeRun {
|
|
|
738
753
|
}
|
|
739
754
|
|
|
740
755
|
/**
|
|
741
|
-
* A
|
|
742
|
-
*
|
|
743
|
-
*
|
|
744
|
-
*
|
|
745
|
-
*
|
|
746
|
-
* and the next reading spans both.
|
|
756
|
+
* A published piece is measured against this run's last baseline over its
|
|
757
|
+
* own working time. The initial baseline is the run start, advanced to the
|
|
758
|
+
* latest known progress when input resumes, so a one-piece run can be
|
|
759
|
+
* measured even when FFmpeg reports progress only once. Input waits remain
|
|
760
|
+
* excluded by RunClock.
|
|
747
761
|
*/
|
|
748
762
|
#measureSpeed() {
|
|
749
763
|
const sample = this.#workSample;
|
|
750
764
|
if (sample === null) {
|
|
751
765
|
return;
|
|
752
766
|
}
|
|
753
|
-
if (this.#pieceSample === null) {
|
|
754
|
-
this.#pieceSample = sample;
|
|
755
|
-
return;
|
|
756
|
-
}
|
|
757
767
|
const speed = speedFromWork(this.#pieceSample, sample);
|
|
758
768
|
if (speed !== null) {
|
|
759
769
|
this.#speedReading = { speed, at: sample.at };
|
|
760
770
|
this.#pieceSample = sample;
|
|
771
|
+
this.#speedMeasurementPending = false;
|
|
761
772
|
this.onSpeedMeasured(this.speedReading);
|
|
762
773
|
}
|
|
763
774
|
}
|
|
@@ -936,7 +936,7 @@ export class ViewerRequests {
|
|
|
936
936
|
const tracks = [];
|
|
937
937
|
for (const output of outputs) {
|
|
938
938
|
const trackProgress = output === session ? progress : this.#host.encodeRuns.progressOf(output);
|
|
939
|
-
const trackRates = this.#trackRateReadings(output, now
|
|
939
|
+
const trackRates = this.#trackRateReadings(output, now);
|
|
940
940
|
const trackName = output === session ? "video" : "audio";
|
|
941
941
|
// The page states the offset its player applied to this track, and the
|
|
942
942
|
// init it applied it to is the session's. Until both are known, the
|
|
@@ -1311,8 +1311,7 @@ export class ViewerRequests {
|
|
|
1311
1311
|
const id = typeof consumerId === "string" ? consumerId : "";
|
|
1312
1312
|
let state = byViewer.get(id);
|
|
1313
1313
|
if (!state) {
|
|
1314
|
-
state = { link: new this.#host.playbackReadiness.RateTrend(this.#host.lookaheadSeconds), sources: new Map()
|
|
1315
|
-
trackProgress: new WeakMap() };
|
|
1314
|
+
state = { link: new this.#host.playbackReadiness.RateTrend(this.#host.lookaheadSeconds), sources: new Map() };
|
|
1316
1315
|
byViewer.set(id, state);
|
|
1317
1316
|
}
|
|
1318
1317
|
return state;
|
|
@@ -1325,7 +1324,7 @@ export class ViewerRequests {
|
|
|
1325
1324
|
* runs of one output can report out of order, so a summary kept and added to
|
|
1326
1325
|
* would drop the later-told, earlier-taken one (torrent-tv/meta#178).
|
|
1327
1326
|
*/
|
|
1328
|
-
#trackRateReadings(output, now
|
|
1327
|
+
#trackRateReadings(output, now) {
|
|
1329
1328
|
const readings = (this.#host.encodeSpeedReadingsOf?.(output) ?? []).filter(reading => reading.at <= now);
|
|
1330
1329
|
const summary = readings.length > 0 ? this.#host.playbackReadiness.rateSummaryOf(readings) : null;
|
|
1331
1330
|
if (summary) return summary;
|
|
@@ -1340,21 +1339,9 @@ export class ViewerRequests {
|
|
|
1340
1339
|
seed.add(now, projected);
|
|
1341
1340
|
return seed.snapshot();
|
|
1342
1341
|
}
|
|
1343
|
-
//
|
|
1344
|
-
//
|
|
1345
|
-
//
|
|
1346
|
-
// inflate, the speed used by the forecast.
|
|
1347
|
-
if (output?.spec?.carries === "audio-only") {
|
|
1348
|
-
let trend = measurement.trackProgress.get(output);
|
|
1349
|
-
if (!trend) {
|
|
1350
|
-
trend = new this.#host.playbackReadiness.ProgressRate(
|
|
1351
|
-
this.#host.lookaheadSeconds,
|
|
1352
|
-
this.#host.segmentDurationSec
|
|
1353
|
-
);
|
|
1354
|
-
measurement.trackProgress.set(output, trend);
|
|
1355
|
-
}
|
|
1356
|
-
return trend.note(now, progress?.processedSeconds, progress?.startPositionSeconds);
|
|
1357
|
-
}
|
|
1342
|
+
// Progress over wall time includes source waits, which the forecast prices
|
|
1343
|
+
// separately. Keep the rate unknown until a measured or calibrated
|
|
1344
|
+
// processing rate exists; single-piece runs now publish their own rate.
|
|
1358
1345
|
return null;
|
|
1359
1346
|
}
|
|
1360
1347
|
|
|
@@ -72,7 +72,7 @@ import { ViewerRequests } from "./ViewerRequests.js";
|
|
|
72
72
|
import { OutputLifecycle } from "./OutputLifecycle.js";
|
|
73
73
|
import { SegmentServing } from "./SegmentServing.js";
|
|
74
74
|
import { audioStartSecondsFor } from "../viewer/audio-start.js";
|
|
75
|
-
import { forecastRate, rateSummaryOf, predictPlaybackReadiness,
|
|
75
|
+
import { forecastRate, rateSummaryOf, predictPlaybackReadiness, RateTrend } from "../viewer/playback-readiness.js";
|
|
76
76
|
import { audioRenditionName } from "../media/audio-inventory.js";
|
|
77
77
|
import { Renditions } from "../encode/Renditions.js";
|
|
78
78
|
import { CushionReport, LOOKAHEAD_PAUSE_SECONDS } from "../encode/CushionReport.js";
|
|
@@ -640,7 +640,7 @@ export function wireOutputs({
|
|
|
640
640
|
get encodeRuns() { return parts.encodeRuns; },
|
|
641
641
|
// When this viewer's playback can start and run to the end: the viewer
|
|
642
642
|
// component's forecast, which models the viewer's browser.
|
|
643
|
-
playbackReadiness: { predict: predictPlaybackReadiness, forecastRate, rateSummaryOf, RateTrend
|
|
643
|
+
playbackReadiness: { predict: predictPlaybackReadiness, forecastRate, rateSummaryOf, RateTrend },
|
|
644
644
|
// Whether the subtitle a viewer chose has been read where they stand — the
|
|
645
645
|
// source preparation's answer, set on these parts by the assembly.
|
|
646
646
|
subtitleReadyFor: (viewer) => parts.subtitleReadyFor?.(viewer) ?? true,
|
|
@@ -110,44 +110,6 @@ export class RateTrend {
|
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
/**
|
|
114
|
-
* Rates the media an output actually produced between progress reports.
|
|
115
|
-
* Unchanged progress keeps its original time, so a source wait is included
|
|
116
|
-
* instead of being mistaken for encoder throughput.
|
|
117
|
-
*/
|
|
118
|
-
export class ProgressRate {
|
|
119
|
-
#trend;
|
|
120
|
-
#horizonSeconds;
|
|
121
|
-
#adjacentRunSeconds;
|
|
122
|
-
#previous = null;
|
|
123
|
-
|
|
124
|
-
constructor(horizonSeconds, adjacentRunSeconds = 4) {
|
|
125
|
-
this.#horizonSeconds = horizonSeconds;
|
|
126
|
-
this.#adjacentRunSeconds = Number.isFinite(adjacentRunSeconds) && adjacentRunSeconds > 0
|
|
127
|
-
? adjacentRunSeconds
|
|
128
|
-
: 4;
|
|
129
|
-
this.#trend = new RateTrend(horizonSeconds);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
note(at, processedSeconds, startPositionSeconds) {
|
|
133
|
-
if (!Number.isFinite(at) || !Number.isFinite(processedSeconds)) return this.#trend.snapshot();
|
|
134
|
-
const start = Number.isFinite(startPositionSeconds) ? startPositionSeconds : 0;
|
|
135
|
-
const discontinuousStart = this.#previous && start !== this.#previous.start &&
|
|
136
|
-
(start < this.#previous.start || start > this.#previous.processedSeconds + this.#adjacentRunSeconds);
|
|
137
|
-
if (!this.#previous || discontinuousStart || processedSeconds < this.#previous.processedSeconds) {
|
|
138
|
-
this.#previous = { at, processedSeconds, start };
|
|
139
|
-
this.#trend = new RateTrend(this.#horizonSeconds);
|
|
140
|
-
return this.#trend.snapshot();
|
|
141
|
-
}
|
|
142
|
-
const elapsedMs = at - this.#previous.at;
|
|
143
|
-
if (processedSeconds > this.#previous.processedSeconds && elapsedMs > 0) {
|
|
144
|
-
this.#trend.add(at, (processedSeconds - this.#previous.processedSeconds) * 1000 / elapsedMs);
|
|
145
|
-
this.#previous = { at, processedSeconds, start };
|
|
146
|
-
}
|
|
147
|
-
return this.#trend.snapshot();
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
113
|
/**
|
|
152
114
|
* @param {object} input
|
|
153
115
|
* @param {number} input.positionSeconds
|