fix(plex): the slow line says both rates in one unit
Each rate took its own unit, so a link under a megabit read "needs 1.4 Mbps, 800 kbps arrives". `fmtBitrates` picks the unit the largest takes for all of them. The away-window header now says what ends an absence outside hls.js (the answer) and after the error (the viewer's retry).
This commit is contained in:
@@ -31,10 +31,12 @@ export const RECLAIM_WINDOW_MS = 120_000;
|
||||
// less often, and says it cannot play only once the server has been away this long. It used to say
|
||||
// so at the first 502, over a picture still playing from the buffer, and never asked again.
|
||||
//
|
||||
// The absence ends when MEDIA arrives again (a fragment loads), not when a session request is
|
||||
// answered: a server that answers every session and delivers none of them is still not serving, and
|
||||
// ending the absence at each answer restarted the stream forever. The same window bounds a link too
|
||||
// slow for the stream (see `slowLink`).
|
||||
// Under hls.js the absence ends when MEDIA arrives again (a fragment loads), not when a session
|
||||
// request is answered: a server that answers every session and delivers none of them is still not
|
||||
// serving, and ending the absence at each answer restarted the stream forever. What hls.js does not
|
||||
// play (a direct file, HLS the browser plays natively) reports no fragments, so there the answer
|
||||
// ends it (`answered`); and the viewer trying again after the error starts a window of its own
|
||||
// (`start`). The same window bounds a link too slow for the stream (see `slowLink`).
|
||||
export const AWAY_WINDOW_MS = 180_000;
|
||||
const AWAY_RETRY_MS = [2_000, 4_000, 8_000, 15_000];
|
||||
const SERVER_AWAY = new Set([0, 502, 503, 504]);
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
buildStatRows,
|
||||
createFragMeter,
|
||||
fmtBitrate,
|
||||
fmtBitrates,
|
||||
fmtBufferSecs,
|
||||
fmtClock,
|
||||
fmtCodec,
|
||||
@@ -28,6 +29,13 @@ describe("plexStats formatters", () => {
|
||||
expect(fmtBitrate(4_200_000, "hu")).toBe("4,2 Mbps"); // the UI language's decimal mark
|
||||
});
|
||||
|
||||
it("fmtBitrates says rates side by side in the unit the largest takes", () => {
|
||||
expect(fmtBitrates([1_400_000, 800_000], "en")).toEqual(["1.4 Mbps", "0.8 Mbps"]);
|
||||
expect(fmtBitrates([1_400_000, 800_000], "hu")).toEqual(["1,4 Mbps", "0,8 Mbps"]);
|
||||
expect(fmtBitrates([900_000, 400_000], "en")).toEqual(["900 kbps", "400 kbps"]);
|
||||
expect(fmtBitrates([2_000_000, 0], "en")).toEqual(["2.0 Mbps", DASH]);
|
||||
});
|
||||
|
||||
it("fmtBufferSecs shows one decimal, dashes negatives/non-finite", () => {
|
||||
expect(fmtBufferSecs(12.34, "en")).toBe("12.3 sec");
|
||||
expect(fmtBufferSecs(0, "en")).toBe("0.0 sec");
|
||||
|
||||
@@ -64,6 +64,16 @@ export function fmtBitrate(bps: number | undefined, lang: string): string {
|
||||
return `${formatNumber(Math.round(bps / 1000), lang)} kbps`;
|
||||
}
|
||||
|
||||
// Rates read side by side ("needs 1.4 Mbps, 0.8 Mbps arrives"): one unit for all of them, the one
|
||||
// the largest would take alone, so they compare at a glance — never "1.4 Mbps" beside "800 kbps".
|
||||
export function fmtBitrates(rates: number[], lang: string): string[] {
|
||||
const largest = Math.max(0, ...rates);
|
||||
if (largest < 1_000_000) return rates.map((bps) => fmtBitrate(bps, lang));
|
||||
return rates.map((bps) =>
|
||||
bps > 0 ? `${formatDecimal(bps / 1_000_000, lang)} Mbps` : fmtBitrate(bps, lang),
|
||||
);
|
||||
}
|
||||
|
||||
// Seconds of buffer ahead of the playhead → "12.3 sec" / "12,3 mp".
|
||||
export function fmtBufferSecs(s: number, lang: string): string {
|
||||
if (!Number.isFinite(s) || s < 0) return DASH;
|
||||
|
||||
Reference in New Issue
Block a user