@torrent-tv/proxy 2.105.3 → 2.105.4
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
|
@@ -147,7 +147,8 @@ export class ViewerRequests {
|
|
|
147
147
|
const seconds = earlyWaitRemainingSeconds(totalSeconds, elapsedMs);
|
|
148
148
|
if (!Number.isFinite(seconds) || seconds < 0) return null;
|
|
149
149
|
this.#logEarlyForecast(context, { elapsedMs, seconds, totalSeconds, historical,
|
|
150
|
-
firstRequiredSegmentsSeconds: context.activeFirstRequiredSegmentsSeconds
|
|
150
|
+
firstRequiredSegmentsSeconds: context.activeFirstRequiredSegmentsSeconds,
|
|
151
|
+
firstRequiredStages: context.activeFirstRequiredStages });
|
|
151
152
|
return { version: 1, waitId, revision: context.revision, remainingSeconds: seconds, ageMs: 0 };
|
|
152
153
|
}
|
|
153
154
|
|
|
@@ -192,13 +193,19 @@ export class ViewerRequests {
|
|
|
192
193
|
const elapsedMs = waitElapsedSince(context, waitElapsedMs);
|
|
193
194
|
const measuredWorkSeconds = processing.firstRequiredSegmentsSeconds;
|
|
194
195
|
const hasActiveEstimate = Number.isFinite(measuredWorkSeconds) && measuredWorkSeconds >= 0;
|
|
196
|
+
const stagesChanged = Array.isArray(processing.firstRequiredStages) &&
|
|
197
|
+
JSON.stringify(processing.firstRequiredStages) !== JSON.stringify(context.activeFirstRequiredStages);
|
|
195
198
|
const activeTotalSeconds = hasActiveEstimate
|
|
196
199
|
? elapsedMs / 1000 + measuredWorkSeconds
|
|
197
200
|
: context.activeTotalSeconds;
|
|
198
|
-
const activeEstimateChanged = hasActiveEstimate &&
|
|
201
|
+
const activeEstimateChanged = hasActiveEstimate &&
|
|
202
|
+
(activeTotalSeconds !== context.activeTotalSeconds || stagesChanged);
|
|
199
203
|
if (hasActiveEstimate) {
|
|
200
204
|
context.activeTotalSeconds = activeTotalSeconds;
|
|
201
205
|
context.activeFirstRequiredSegmentsSeconds = measuredWorkSeconds;
|
|
206
|
+
if (Array.isArray(processing.firstRequiredStages)) {
|
|
207
|
+
context.activeFirstRequiredStages = processing.firstRequiredStages;
|
|
208
|
+
}
|
|
202
209
|
}
|
|
203
210
|
const changed = featuresChanged || activeEstimateChanged;
|
|
204
211
|
if (sameWait && changed) context.revision += 1;
|
|
@@ -213,7 +220,8 @@ export class ViewerRequests {
|
|
|
213
220
|
const seconds = earlyWaitRemainingSeconds(totalSeconds, elapsedMs);
|
|
214
221
|
if (!Number.isFinite(seconds) || seconds < 0) return null;
|
|
215
222
|
this.#logEarlyForecast(context, { elapsedMs, seconds, totalSeconds, historical,
|
|
216
|
-
firstRequiredSegmentsSeconds: hasActiveEstimate ? measuredWorkSeconds : context.activeFirstRequiredSegmentsSeconds
|
|
223
|
+
firstRequiredSegmentsSeconds: hasActiveEstimate ? measuredWorkSeconds : context.activeFirstRequiredSegmentsSeconds,
|
|
224
|
+
firstRequiredStages: context.activeFirstRequiredStages });
|
|
217
225
|
return { version: 1, waitId, revision: context.revision, remainingSeconds: seconds, ageMs: 0 };
|
|
218
226
|
}
|
|
219
227
|
|
|
@@ -222,11 +230,12 @@ export class ViewerRequests {
|
|
|
222
230
|
return Number.isFinite(estimate) ? { seconds: estimate, stagesMs: null, observations: null } : estimate;
|
|
223
231
|
}
|
|
224
232
|
|
|
225
|
-
#logEarlyForecast(context, { elapsedMs, seconds, totalSeconds, historical, firstRequiredSegmentsSeconds
|
|
233
|
+
#logEarlyForecast(context, { elapsedMs, seconds, totalSeconds, historical, firstRequiredSegmentsSeconds,
|
|
234
|
+
firstRequiredStages }) {
|
|
226
235
|
if (context.loggedForecastRevision === context.revision) return;
|
|
227
236
|
const basis = Number.isFinite(firstRequiredSegmentsSeconds) ? "first-required" : "historical-stages";
|
|
228
237
|
const estimate = basis === "first-required"
|
|
229
|
-
? { firstRequiredSegmentsSeconds }
|
|
238
|
+
? { firstRequiredSegmentsSeconds, ...(Array.isArray(firstRequiredStages) ? { firstRequiredStages } : {}) }
|
|
230
239
|
: { stagesMs: historical?.stagesMs ?? null, observations: historical?.observations ?? null };
|
|
231
240
|
logger.info(`early wait forecast ${JSON.stringify({ waitId: context.waitId, consumer: context.consumerId,
|
|
232
241
|
generation: context.generation, features: context.features, elapsedMs, revision: context.revision,
|
|
@@ -1062,7 +1071,8 @@ export class ViewerRequests {
|
|
|
1062
1071
|
audioOperation: audioOperation?.operation,
|
|
1063
1072
|
videoSpeedX: videoOperation?.speed,
|
|
1064
1073
|
audioSpeedX: audioOperation?.speed,
|
|
1065
|
-
firstRequiredSegmentsSeconds: forecast.firstRequiredSegmentsSeconds
|
|
1074
|
+
firstRequiredSegmentsSeconds: forecast.firstRequiredSegmentsSeconds,
|
|
1075
|
+
firstRequiredStages: forecast.firstRequiredStages
|
|
1066
1076
|
} });
|
|
1067
1077
|
if (waitEstimate) forecast.waitEstimate = waitEstimate;
|
|
1068
1078
|
}
|
|
@@ -1105,6 +1115,7 @@ export class ViewerRequests {
|
|
|
1105
1115
|
const holding = forecast.holdingBy;
|
|
1106
1116
|
this.#host.noteViewerWaiting?.(consumerId || session.id,
|
|
1107
1117
|
holding?.kind === "production" ? { address: holding.track, index: holding.segment } : null);
|
|
1118
|
+
delete forecast.firstRequiredStages;
|
|
1108
1119
|
return forecast;
|
|
1109
1120
|
}
|
|
1110
1121
|
|
|
@@ -463,23 +463,28 @@ export function predictPlaybackReadiness(input = {}) {
|
|
|
463
463
|
// Production accepts a complete held segment input. It cannot overlap its
|
|
464
464
|
// own source download; reused input retains its original arrival time.
|
|
465
465
|
const processing = trackState[trackIndex].curve;
|
|
466
|
-
let
|
|
466
|
+
let runAdmissionAt = null;
|
|
467
|
+
let runOpenSeconds = 0;
|
|
468
|
+
let productionStartAt = Math.max(trackFinish[trackIndex], sourceArrivalAt);
|
|
467
469
|
if (!job.produced && encodeWork > 0) {
|
|
468
470
|
const run = runStarts[trackIndex];
|
|
469
471
|
const live = run.spans.some(span => segment.startSeconds >= span.start && segment.startSeconds < span.end);
|
|
470
472
|
if (!live && (run.admittedAt === null || sourceArrivalAt > run.admittedAt)) {
|
|
471
|
-
|
|
472
|
-
admittedAt
|
|
473
|
+
runAdmissionAt = productionStartAt;
|
|
474
|
+
run.admittedAt = runAdmissionAt;
|
|
475
|
+
runOpenSeconds = run.openSeconds;
|
|
476
|
+
productionStartAt += runOpenSeconds;
|
|
473
477
|
}
|
|
474
478
|
}
|
|
475
|
-
const producedAt = job.produced || encodeWork === 0 ? 0 : processing.finish(encodeWork,
|
|
479
|
+
const producedAt = job.produced || encodeWork === 0 ? 0 : processing.finish(encodeWork, productionStartAt);
|
|
476
480
|
trackFinish[trackIndex] = Math.max(trackFinish[trackIndex], producedAt);
|
|
477
481
|
const sourcePlanned = sourceArrivalAt > 0 && plan.finish >= sourceArrivalAt;
|
|
478
482
|
productions.push({ at: producedAt, trackIndex, segment, requiresSource: sourceArrivalAt > 0, sourceArrivalAt,
|
|
479
|
-
sourcePlanned });
|
|
483
|
+
sourcePlanned, runAdmissionAt, runOpenSeconds, productionStartAt, encodeWork });
|
|
480
484
|
const deliveryAt = link.finish(sizeBits, Math.max(transferFinish, producedAt));
|
|
481
485
|
transferFinish = deliveryAt;
|
|
482
|
-
|
|
486
|
+
const appendSeconds = finiteNonNegative(track.appendSeconds);
|
|
487
|
+
completions.push({ at: deliveryAt + appendSeconds, deliveryAt, appendSeconds, trackIndex, segment, sourceArrivalAt,
|
|
483
488
|
sourcePlanned });
|
|
484
489
|
}
|
|
485
490
|
|
|
@@ -547,13 +552,32 @@ export function predictPlaybackReadiness(input = {}) {
|
|
|
547
552
|
...(stop.unavailableSource ? { unavailableSource: stop.unavailableSource } : {}) };
|
|
548
553
|
}
|
|
549
554
|
}
|
|
550
|
-
const
|
|
555
|
+
const firstRequiredStages = trackState.slice(0, requiredTracks).map((state, trackIndex) => {
|
|
551
556
|
const first = state.segments.find(segment => segment.endSeconds > position);
|
|
552
|
-
if (!first) return
|
|
553
|
-
|
|
557
|
+
if (!first) return { track: state.track.id ?? trackIndex, segment: null, alreadyBuffered: true,
|
|
558
|
+
sourceArrivalSeconds: 0, runAdmissionSeconds: null, runOpenSeconds: 0, productionStartSeconds: 0,
|
|
559
|
+
productionCompleteSeconds: 0, deliveryCompleteSeconds: 0, appendSeconds: 0, completionSeconds: 0 };
|
|
560
|
+
if (first.ranges.every(range => contains(state.clientRanges, range.start, range.end))) {
|
|
561
|
+
return { track: state.track.id ?? trackIndex, segment: first.index, alreadyBuffered: true,
|
|
562
|
+
sourceArrivalSeconds: 0, runAdmissionSeconds: null, runOpenSeconds: 0, productionStartSeconds: 0,
|
|
563
|
+
productionCompleteSeconds: 0, deliveryCompleteSeconds: 0, appendSeconds: 0, completionSeconds: 0 };
|
|
564
|
+
}
|
|
554
565
|
const completion = completions.find(item => item.trackIndex === trackIndex && item.segment.index === first.index);
|
|
555
|
-
|
|
566
|
+
if (!completion) return { track: state.track.id ?? trackIndex, segment: first.index, alreadyBuffered: false,
|
|
567
|
+
sourceArrivalSeconds: null, runAdmissionSeconds: null, runOpenSeconds: null, productionStartSeconds: null,
|
|
568
|
+
productionCompleteSeconds: null, deliveryCompleteSeconds: null, appendSeconds: null, completionSeconds: null };
|
|
569
|
+
const production = productions.find(item => item.trackIndex === trackIndex && item.segment.index === first.index);
|
|
570
|
+
return { track: state.track.id ?? trackIndex, segment: first.index, alreadyBuffered: false,
|
|
571
|
+
sourceArrivalSeconds: production?.sourceArrivalAt ?? 0,
|
|
572
|
+
runAdmissionSeconds: production?.runAdmissionAt ?? null,
|
|
573
|
+
runOpenSeconds: production?.runOpenSeconds ?? 0,
|
|
574
|
+
productionStartSeconds: production?.productionStartAt ?? null,
|
|
575
|
+
productionCompleteSeconds: production?.at ?? null,
|
|
576
|
+
deliveryCompleteSeconds: completion.deliveryAt,
|
|
577
|
+
appendSeconds: completion.appendSeconds,
|
|
578
|
+
completionSeconds: completion.at };
|
|
556
579
|
});
|
|
580
|
+
const firstRequiredTrackSeconds = firstRequiredStages.map(stage => stage.completionSeconds);
|
|
557
581
|
const firstRequiredSegmentsSeconds = firstRequiredTrackSeconds.every(Number.isFinite)
|
|
558
582
|
? Math.max(0, ...firstRequiredTrackSeconds) : null;
|
|
559
583
|
const delay = Math.max(0, ...constraints);
|
|
@@ -564,6 +588,7 @@ export function predictPlaybackReadiness(input = {}) {
|
|
|
564
588
|
Number.isFinite(delay) ? 'minimum-safe-delay' : 'service-not-advancing', preparedSegments);
|
|
565
589
|
return { ...forecast, measuredAt: now, predictedStartAt: Number.isFinite(delay) ? now + delay * 1000 : null,
|
|
566
590
|
firstRequiredSegmentsSeconds,
|
|
591
|
+
firstRequiredStages,
|
|
567
592
|
// The piece whose `kind` — its production, or its delivery to the page —
|
|
568
593
|
// comes `atSeconds` from now while the media before it lasts
|
|
569
594
|
// `aheadSeconds`: the difference is the delay.
|