feat(plex): surface playback errors + media-mount status in the UI
PlexPlayer: when the stream session can't start, show why instead of an eternal spinner — 404 (physical file unreachable: missing media mount / wrong path map), 501 (format needs transcoding), or a generic failure; also catch fatal hls.js errors. ConfigPanel: render the Test-connection media_check (mount OK, or a warning naming the unreadable local path). New plex/config i18n in en/hu/de.
This commit is contained in:
parent
2138f3c357
commit
fdd39ef3e4
9 changed files with 59 additions and 7 deletions
|
|
@ -56,6 +56,7 @@ export default function PlexPlayer({ itemId, onClose }: Props) {
|
|||
const [muted, setMuted] = useState(false);
|
||||
const [fs, setFs] = useState(false);
|
||||
const [ready, setReady] = useState(false);
|
||||
const [loadError, setLoadError] = useState<string | null>(null);
|
||||
const [uiVisible, setUiVisible] = useState(true);
|
||||
const hideTimer = useRef<number | null>(null);
|
||||
// Selected audio / subtitle stream ordinals (null audio = default first; null subtitle = off).
|
||||
|
|
@ -83,10 +84,21 @@ export default function PlexPlayer({ itemId, onClose }: Props) {
|
|||
const video = videoRef.current;
|
||||
if (!video) return;
|
||||
setReady(false);
|
||||
setLoadError(null);
|
||||
let sess;
|
||||
try {
|
||||
sess = await api.plexSession(id, startAt, audioRef.current, subRef.current);
|
||||
} catch {
|
||||
} catch (e: any) {
|
||||
// Surface WHY playback can't start instead of an eternal spinner. 404 = the physical file
|
||||
// isn't reachable (missing media bind-mount or a wrong plex_path_map); 501 = the codec
|
||||
// needs full transcoding (a later phase).
|
||||
setLoadError(
|
||||
e?.status === 501
|
||||
? t("plex.player.errTranscode")
|
||||
: e?.status === 404
|
||||
? t("plex.player.errNotFound")
|
||||
: t("plex.player.errGeneric"),
|
||||
);
|
||||
return;
|
||||
}
|
||||
sessionStartRef.current = sess.start_s;
|
||||
|
|
@ -112,6 +124,11 @@ export default function PlexPlayer({ itemId, onClose }: Props) {
|
|||
setReady(true);
|
||||
video.play().catch(() => {});
|
||||
});
|
||||
// A fatal HLS error (e.g. the remux session died / the file became unreadable) would
|
||||
// otherwise leave the spinner up forever — surface it instead.
|
||||
hls.on(Hls.Events.ERROR, (_e, data) => {
|
||||
if (data.fatal) setLoadError(t("plex.player.errGeneric"));
|
||||
});
|
||||
} else {
|
||||
// direct file (or Safari native HLS)
|
||||
video.src = sess.url;
|
||||
|
|
@ -125,7 +142,7 @@ export default function PlexPlayer({ itemId, onClose }: Props) {
|
|||
video.addEventListener("loadedmetadata", onMeta);
|
||||
}
|
||||
},
|
||||
[id],
|
||||
[id, t],
|
||||
);
|
||||
|
||||
// Start playback when the detail arrives (resume from the saved position unless already watched).
|
||||
|
|
@ -407,8 +424,12 @@ export default function PlexPlayer({ itemId, onClose }: Props) {
|
|||
/>
|
||||
|
||||
{!ready && (
|
||||
<div className="absolute inset-0 grid place-items-center text-white/70 text-sm pointer-events-none">
|
||||
{t("plex.player.loading")}
|
||||
<div className="absolute inset-0 grid place-items-center px-6 text-center text-sm pointer-events-none">
|
||||
{loadError ? (
|
||||
<span className="max-w-md text-red-300">{loadError}</span>
|
||||
) : (
|
||||
<span className="text-white/70">{t("plex.player.loading")}</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue