Files
siftlode/frontend/src/lib/plexStats.test.ts
T
peter 4aaaaa7860 chore(comments): drop epic, sprint and review-round ids from the code
The code cited its plan instead of saying why: `E28 review J4`, `(R6 S3b)`, `S1 justified…`,
`deep-review round 5`. That plan lives in the private ops repo, so for every reader of this one the
citation was noise, and a sentence whose subject was the plan step ("round 2 showed…") hid its
lesson behind an id. Each one is gone and the "why" kept: a bare citation is deleted, a sentence
carrying a lesson is reworded around it ("a later attempt showed…").

Measured (`scripts/audit_metrics.py`, product code): 652 lines in 270 files carried an epic code by
the definition the next commit holds (507 / 241 by the old, two-digit-only one) -> 0. Across all
code, tests, scripts and configs: ~1100 epic-code lines plus ~320 sprint/round ids.

Behaviour unchanged, checked mechanically: every changed .py file has the same AST once docstrings
are blanked; css/sh/toml/yml/json files are identical once comments are stripped; the only
non-comment changes in TS/JS are test titles, one description string in a contract table, and five
ESLint messages, whose text is all the gates snapshot diff holds. Not touched: Markdown (docs and
the changelog keep their history), Alembic migrations, TV episode notation (`S2 · E5`), DESIGN.md
ids (`C1`, `B4`, `S3h`), tracker ids (`SEC-16`, `DX-17`).

One stale claim fixed on the way: test_hls_reaper's docstring said the player checkpoints "every
10 s whether or not it is playing", which plex/stream.py calls wrong; it now says what the code does.
2026-10-05 00:19:39 +02:00

251 lines
9.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, expect, it, vi } from "vitest";
// format.ts boots i18n on import (localStorage); the formatters here take the language as an argument.
vi.mock("../i18n", () => ({ default: { t: (k: string) => k } }));
import {
buildStatRows,
createFragMeter,
fmtBitrate,
fmtBufferSecs,
fmtClock,
fmtCodec,
fmtFrames,
fmtLevel,
fmtMode,
fmtRes,
type PlexStatsInput,
} from "./plexStats";
const DASH = "—";
describe("plexStats formatters", () => {
it("fmtBitrate scales kbps/Mbps and dashes zero/undefined", () => {
expect(fmtBitrate(undefined, "en")).toBe(DASH);
expect(fmtBitrate(0, "en")).toBe(DASH);
expect(fmtBitrate(820_000, "en")).toBe("820 kbps");
expect(fmtBitrate(4_200_000, "en")).toBe("4.2 Mbps");
expect(fmtBitrate(1_000_000, "en")).toBe("1.0 Mbps");
expect(fmtBitrate(4_200_000, "hu")).toBe("4,2 Mbps"); // the UI language's decimal mark
});
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");
expect(fmtBufferSecs(12.34, "hu")).toBe("12,3 mp");
expect(fmtBufferSecs(-1, "en")).toBe(DASH);
expect(fmtBufferSecs(Infinity, "en")).toBe(DASH);
});
it("fmtFrames shows ratio + drop %, dashes when nothing decoded", () => {
expect(fmtFrames(0, 0, "en")).toBe(DASH);
expect(fmtFrames(0, 1000, "en")).toBe("0 / 1,000 (0.00%)");
expect(fmtFrames(5, 1000, "en")).toBe("5 / 1,000 (0.50%)");
expect(fmtFrames(30, 1000, "en")).toBe("30 / 1,000 (3.0%)");
expect(fmtFrames(30, 1000, "hu")).toBe("30 / 1000 (3,0%)");
});
it("fmtRes dashes an undecoded 0×0 frame", () => {
expect(fmtRes(1920, 1080)).toBe("1920×1080");
expect(fmtRes(0, 0)).toBe(DASH);
expect(fmtRes(1920, 0)).toBe(DASH);
});
it("fmtClock renders m:ss and dashes bad input", () => {
expect(fmtClock(0)).toBe("0:00");
expect(fmtClock(9)).toBe("0:09");
expect(fmtClock(75)).toBe("1:15");
expect(fmtClock(3661)).toBe("61:01");
expect(fmtClock(-1)).toBe(DASH);
});
it("fmtLevel is 1-based with resolution, handles auto and empty", () => {
expect(fmtLevel(null)).toBe(DASH);
expect(fmtLevel({ index: 1, total: 4, width: 1280, height: 720 })).toBe("2 / 4 · 1280×720");
expect(fmtLevel({ index: -1, total: 3 })).toBe("auto / 3");
expect(fmtLevel({ index: 0, total: 0 })).toBe(DASH);
});
it("fmtCodec strips profile suffixes and dashes when absent", () => {
expect(fmtCodec("avc1.640028", "mp4a.40.2")).toBe("avc1 / mp4a");
expect(fmtCodec("hvc1.1.6.L120", undefined)).toBe("hvc1");
expect(fmtCodec(undefined, undefined)).toBe(DASH);
});
});
describe("buildStatRows", () => {
const base: PlexStatsInput = {
lang: "en",
playable: "remux",
transport: "hls",
videoWidth: 1280,
videoHeight: 720,
droppedFrames: 2,
totalFrames: 500,
bufferAheadS: 8.2,
hlsLevel: { index: 0, total: 1, width: 1280, height: 720, bitrate: 3_000_000 },
sessionStartS: 65,
abs: 130,
duration: 5214,
};
it("includes the HLS-only rows (codec/bitrate/level) for hls transport", () => {
const keys = buildStatRows(base).map((r) => r.key);
expect(keys).toEqual([
"mode",
"transport",
"res",
"codec",
"bitrate",
"level",
"audio",
"subtitle",
"frames",
"buffer",
"offset",
"position",
]);
});
it("drops the HLS-only rows for direct play", () => {
const keys = buildStatRows({ ...base, transport: "direct", hlsLevel: null }).map((r) => r.key);
expect(keys).not.toContain("codec");
expect(keys).not.toContain("bitrate");
expect(keys).not.toContain("level");
expect(keys).toContain("res");
});
it("surfaces the classification as the mode value and formats position", () => {
const rows = buildStatRows({ ...base, playable: "transcode" });
expect(rows.find((r) => r.key === "mode")?.value).toBe("transcode");
expect(rows.find((r) => r.key === "offset")?.value).toBe("1:05");
expect(rows.find((r) => r.key === "position")?.value).toBe("2:10 / 86:54");
});
it("appends the transcode encoder to the mode row when known", () => {
const gpu = buildStatRows({ ...base, playable: "transcode", hwEncoder: true });
expect(gpu.find((r) => r.key === "mode")?.value).toBe("transcode · GPU");
const cpu = buildStatRows({ ...base, playable: "transcode", hwEncoder: false });
expect(cpu.find((r) => r.key === "mode")?.value).toBe("transcode · CPU");
});
it("tags the mode row with HDR→SDR for a HW tonemap session", () => {
const rows = buildStatRows({ ...base, playable: "transcode", hwEncoder: true, tonemap: true });
expect(rows.find((r) => r.key === "mode")?.value).toBe("transcode · GPU · HDR→SDR");
});
it("shows an empty classification honestly as a dash", () => {
expect(buildStatRows({ ...base, playable: "" }).find((r) => r.key === "mode")?.value).toBe(
DASH,
);
});
});
describe("fmtMode", () => {
it("appends the encoder only for a transcode with a known encoder", () => {
expect(fmtMode("transcode", true)).toBe("transcode · GPU");
expect(fmtMode("transcode", false)).toBe("transcode · CPU");
expect(fmtMode("transcode", null)).toBe("transcode"); // encoder unknown → bare mode
expect(fmtMode("transcode")).toBe("transcode");
expect(fmtMode("remux", true)).toBe("remux"); // not a transcode → no encoder suffix
expect(fmtMode("")).toBe(DASH);
// The HDR→SDR tag rides on the GPU encoder token for a tonemap session.
expect(fmtMode("transcode", true, true)).toBe("transcode · GPU · HDR→SDR");
expect(fmtMode("transcode", true, false)).toBe("transcode · GPU");
expect(fmtMode("transcode", null, true)).toBe("transcode"); // no encoder → no tag either
});
});
// --- item 12 of the 2026-09 bug hunt ------------------------------------------------------------
//
// The panel reported "Bitrate: 211 kbps" on EVERY remux, which the viewer rightly refused to
// believe. It was reading the master playlist's BANDWIDTH, and for a remux ffmpeg computes that from
// the only stream whose rate it knows — the `-b:a 192k` AAC track — plus 10%: 192000 × 1.1 = 211200,
// a constant that has nothing to do with the video. And the panel never said which audio or subtitle
// track was playing, though the player can switch both.
describe("measured bitrate", () => {
it("reports bytes per second of MEDIA, not of wall clock", () => {
const m = createFragMeter();
// A 6 s remux segment of 6 MB is 8 Mbps, however fast it happened to download.
m.add({ type: "main", bytes: 6_000_000, durationS: 6 });
expect(m.bitrate()).toBe(8_000_000);
});
it("adds the audio rendition's rate instead of averaging it away", () => {
// Multi-audio sessions ship audio as its own rendition, so both fragment types cover the SAME
// media seconds. Pooling bytes over pooled durations would report half the real bitrate.
const m = createFragMeter();
m.add({ type: "main", bytes: 6_000_000, durationS: 6 });
m.add({ type: "audio", bytes: 150_000, durationS: 6 });
expect(m.bitrate()).toBe(8_000_000 + 200_000);
});
it("has nothing to say before the first fragment, and after a reset", () => {
const m = createFragMeter();
expect(m.bitrate()).toBeUndefined(); // an honest dash — never the manifest's stand-in number
m.add({ type: "main", bytes: 6_000_000, durationS: 6 });
m.reset();
expect(m.bitrate()).toBeUndefined();
});
it("ignores init segments and anything with no media duration", () => {
const m = createFragMeter();
m.add({ type: "main", bytes: 900, durationS: 0 });
m.add({ type: "main", bytes: 0, durationS: 6 });
expect(m.bitrate()).toBeUndefined();
});
it("follows the stream: old fragments fall out of the rolling window", () => {
const m = createFragMeter(2);
m.add({ type: "main", bytes: 60_000_000, durationS: 6 }); // a dense scene
m.add({ type: "main", bytes: 3_000_000, durationS: 6 });
m.add({ type: "main", bytes: 3_000_000, durationS: 6 });
expect(m.bitrate()).toBe(4_000_000); // the 80 Mbps sample is gone, not still weighing it down
});
});
describe("buildStatRows — what is actually playing", () => {
const hls = (over: Partial<PlexStatsInput> = {}): PlexStatsInput => ({
lang: "en",
playable: "remux",
transport: "hls",
videoWidth: 1920,
videoHeight: 1080,
droppedFrames: 6,
totalFrames: 1458,
bufferAheadS: 12,
hlsLevel: { index: 0, total: 1, bitrate: 211_200 },
sessionStartS: 0,
abs: 10,
duration: 8580,
...over,
});
const value = (rows: { key: string; value: string }[], key: string) =>
rows.find((r) => r.key === key)?.value;
it("shows the MEASURED bitrate and never the manifest's 211 kbps stand-in", () => {
const rows = buildStatRows(hls({ measuredBps: 7_900_000 }));
expect(value(rows, "bitrate")).toBe("7.9 Mbps");
});
it("dashes the bitrate until something has been measured", () => {
// The level still carries its 211200 BANDWIDTH here — using it would be the old bug.
expect(value(buildStatRows(hls()), "bitrate")).toBe(DASH);
});
it("names the selected audio and subtitle track", () => {
const rows = buildStatRows(hls({ audioLabel: "Magyar AC3 5.1", subLabel: "Magyar" }));
expect(value(rows, "audio")).toBe("Magyar AC3 5.1");
expect(value(rows, "subtitle")).toBe("Magyar");
});
it("keeps the track rows for direct play too, where there is no HLS level at all", () => {
const rows = buildStatRows(
hls({ transport: "direct", hlsLevel: null, audioLabel: "English", subLabel: "Off" }),
);
expect(rows.some((r) => r.key === "bitrate")).toBe(false); // HLS-only, as before
expect(value(rows, "audio")).toBe("English");
expect(value(rows, "subtitle")).toBe("Off");
});
});