@torrent-tv/proxy 2.99.8 → 2.99.10
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
|
@@ -582,6 +582,10 @@ export class ViewerRequests {
|
|
|
582
582
|
|
|
583
583
|
async #playbackReadinessFor(session, consumerId, progress, outputMbps) {
|
|
584
584
|
const now = Date.now();
|
|
585
|
+
// WHERE AN ANSWER'S TIME GOES, said with each answer. On Home Assistant a
|
|
586
|
+
// held viewer's answer took 11.2 s while its forecast took 0.1-0.4 s and
|
|
587
|
+
// the answer crossed the connection in 21 ms (r50, torrent-tv/meta#178).
|
|
588
|
+
const parts = { enteredAt: performance.now(), statsMs: 0, workerForecastMs: 0, qualityMs: 0 };
|
|
585
589
|
const viewer = consumerId ? this.#host.viewers.get(consumerId) : null;
|
|
586
590
|
const viewerReading = viewer?.linkReading() ?? null;
|
|
587
591
|
const measurement = this.#rateStateFor(session, consumerId);
|
|
@@ -629,12 +633,15 @@ export class ViewerRequests {
|
|
|
629
633
|
continue;
|
|
630
634
|
}
|
|
631
635
|
let stats = null;
|
|
636
|
+
const statsAt = performance.now();
|
|
632
637
|
try {
|
|
633
638
|
stats = await this.#host.getSourceStats?.(output.file.sourceKey, fileIndex,
|
|
634
639
|
{ forecast: !sourceForecasts.has(output.file.sourceKey) }) ?? null;
|
|
635
640
|
} catch {
|
|
636
641
|
stats = null;
|
|
637
642
|
}
|
|
643
|
+
parts.statsMs += performance.now() - statsAt;
|
|
644
|
+
parts.workerForecastMs += Number(stats?.forecastMs) || 0;
|
|
638
645
|
if (!sourceForecasts.has(output.file.sourceKey)) sourceForecasts.set(output.file.sourceKey, stats?.downloadForecast ?? { ranges: [] });
|
|
639
646
|
const sharedForecast = sourceForecasts.get(output.file.sourceKey);
|
|
640
647
|
const downloadForecast = sharedForecast.global ? { measuredAt: sharedForecast.measuredAt,
|
|
@@ -655,9 +662,11 @@ export class ViewerRequests {
|
|
|
655
662
|
});
|
|
656
663
|
}
|
|
657
664
|
|
|
665
|
+
const qualityAt = performance.now();
|
|
658
666
|
const observedMbps = output === session
|
|
659
667
|
? outputMbps
|
|
660
668
|
: await this.#host.quality.observedStreamMbps(output);
|
|
669
|
+
parts.qualityMs += performance.now() - qualityAt;
|
|
661
670
|
const liveRuns = this.#host.encodeRuns.liveRunsOf?.(output) ?? [];
|
|
662
671
|
const processingStarted = liveRuns.some(({ progress }) => progress.processedSeconds > progress.startPositionSeconds);
|
|
663
672
|
const startedAt = liveRuns.find(({ startedAt }) => startedAt > 0)?.startedAt ?? now;
|
|
@@ -734,7 +743,11 @@ export class ViewerRequests {
|
|
|
734
743
|
viewer.subtitle.fileIndex === session.file.fileIndex ? viewer.subtitle : null;
|
|
735
744
|
const startedAt = performance.now();
|
|
736
745
|
const predicted = this.#host.playbackReadiness.predict(input);
|
|
737
|
-
|
|
746
|
+
const predictMs = performance.now() - startedAt;
|
|
747
|
+
this.#noteForecast(predictMs, tracks.map(track => track.geometry?.reason ?? "none"));
|
|
748
|
+
logger.info(`readiness parts ${session.id} consumer=${consumerId}: source stats ${Math.round(parts.statsMs)}ms ` +
|
|
749
|
+
`(the torrent thread's arrival forecast ${Math.round(parts.workerForecastMs)}ms), stream rate ${Math.round(parts.qualityMs)}ms, ` +
|
|
750
|
+
`start forecast ${Math.round(predictMs)}ms, in all ${Math.round(performance.now() - parts.enteredAt)}ms`);
|
|
738
751
|
const forecast = withSubtitleReadiness(predicted, subtitle,
|
|
739
752
|
subtitle ? this.#host.subtitleReadyFor?.(viewer) !== false : true);
|
|
740
753
|
forecast.operations = outputs.flatMap((output, index) => {
|
|
@@ -472,7 +472,9 @@ async function runCommand(command, params, id) {
|
|
|
472
472
|
// second interruption has been seen: one wait shows no interval, and an
|
|
473
473
|
// interval invented from one point is exactly what this work removes.
|
|
474
474
|
const file = Array.isArray(torrent?.files) ? torrent.files[params.fileIndex] : null;
|
|
475
|
+
const forecastAt = performance.now();
|
|
475
476
|
const forecast = file && params.forecast === true ? (await forecastDownloads()).get(torrent) : null;
|
|
477
|
+
const forecastMs = params.forecast === true ? performance.now() - forecastAt : 0;
|
|
476
478
|
const stats = pool.getFileStats(torrent, params.fileIndex);
|
|
477
479
|
const ranges = [];
|
|
478
480
|
if (forecast) for (const [piece, availableAt] of forecast.arrivals) {
|
|
@@ -485,7 +487,10 @@ async function runCommand(command, params, id) {
|
|
|
485
487
|
...(params.forecast === true ? { downloadForecast: {
|
|
486
488
|
measuredAt: forecast?.measuredAt ?? Date.now(), global: true, ranges
|
|
487
489
|
} } : {}),
|
|
488
|
-
supply: supplyFiguresFor(torrent?.infoHash, file?.name, params.segmentSeconds ?? 4)
|
|
490
|
+
supply: supplyFiguresFor(torrent?.infoHash, file?.name, params.segmentSeconds ?? 4),
|
|
491
|
+
// How long this answer waited for the arrival forecast, which the
|
|
492
|
+
// caller says beside its own time (ViewerRequests, torrent-tv/meta#178).
|
|
493
|
+
forecastMs
|
|
489
494
|
};
|
|
490
495
|
}
|
|
491
496
|
|
|
@@ -313,6 +313,14 @@ export function createTunnelClient({
|
|
|
313
313
|
signalRoutes.delete(sessionId);
|
|
314
314
|
}
|
|
315
315
|
}
|
|
316
|
+
// Closed because this proxy is stopping (`disconnect`), which clears the
|
|
317
|
+
// connection before its close arrives. Read as a handover, a stopping
|
|
318
|
+
// proxy said a replacement carried its tunnel while viewers found no
|
|
319
|
+
// proxy at all (r48, torrent-tv/meta#178).
|
|
320
|
+
if (stopped) {
|
|
321
|
+
log(`Tunnel closed (code=${event.code}): this proxy is stopping.`);
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
316
324
|
// A connection this one replaced. The server closes it as soon as the
|
|
317
325
|
// replacement registers, which is the whole point of renewing early —
|
|
318
326
|
// there is nothing to report and nothing to reconnect, because the tunnel
|