fix(plex): a playlist that never answers is asked for again; the slow line reads this stream

A playlist timeout was resumed in the same session, but hls.js reloads no manifest on startLoad: no
request, no error, a spinner for good. It is now asked for again like a server away. An absence
also ended only on a fragment, which native HLS never reports, and a viewer trying again after the
error started with the spent window; both now start a fresh one. The slow-connection meter was
reset only when hls.js was built, so a direct answer showed the previous stream's numbers, and a
replaced session's late fragment still counted; its rates now use the stats overlay's units.
This commit is contained in:
2026-11-07 21:58:36 +01:00
parent a6f1a26658
commit 90931b4750
6 changed files with 133 additions and 33 deletions
+10 -5
View File
@@ -53,7 +53,7 @@ import ClipPanel, { ClipCropFrame, type ClipCrop } from "../../features/player/C
import { markInAt, markOutAt, openAt, type ClipRange } from "../../lib/clipRange";
import { PlexPlayerSettings } from "./PlexPlayerSettings";
import { PlexStatsOverlay } from "./PlexStatsOverlay";
import { createFragMeter } from "../../lib/plexStats";
import { createFragMeter, fmtBitrate } from "../../lib/plexStats";
import { episodeLine } from "../../lib/plexDetail";
import { HlsEngine } from "../../lib/hlsEngine";
import { createHlsPort } from "../../lib/hlsPort";
@@ -103,7 +103,7 @@ import { MenuCheck } from "../../components/ui/MenuCheck";
import { PopoverItem, PopoverSurface } from "../../components/ui/AnchoredPopover";
import { closeThenAct, useAnchoredPopover } from "../../components/ui/anchoredPopoverHooks";
import { Slider } from "../../components/ui/NativeControls";
import { formatMbps, formatUnit } from "../../lib/format";
import { formatUnit } from "../../lib/format";
import { trackLabel } from "../../lib/vocab";
import { msg } from "../../lib/notifications";
@@ -260,7 +260,12 @@ export default function PlexPlayer({
// `req` rethrows fetch's own TypeError when nothing answered at all: that is a status of 0.
statusOf: (e) =>
e instanceof HttpError ? e.status : e instanceof TypeError ? 0 : undefined,
onLoadStart: () => setBuffering(false),
// Every stream asked for (another item, a seek, a restart) is measured from nothing: a
// direct file answering it builds no hls.js, and must not show the previous one's numbers.
onLoadStart: () => {
setBuffering(false);
fragMeterRef.current.reset();
},
onFragLoaded: (frag) => fragMeterRef.current.add(frag),
// Fatal ones are what the viewer saw fail; the recoverable ones hls.js retries by itself only
// matter to a detailed diagnosis.
@@ -1346,8 +1351,8 @@ export default function PlexPlayer({
<span>{t("plex.player.slowLink")}</span>
<span className="text-xs text-white/70">
{t("plex.player.slowLinkRates", {
need: formatMbps(slowRates.need, i18n.language),
got: formatMbps(slowRates.got, i18n.language),
need: fmtBitrate(slowRates.need, i18n.language),
got: fmtBitrate(slowRates.got, i18n.language),
})}
</span>
</>