add UI setting to configure jsmpeg fallback timeout

This commit is contained in:
Josh Hawkins 2025-11-10 18:19:40 -06:00
parent 3392039185
commit 0b43c15602
3 changed files with 60 additions and 3 deletions

View File

@ -8,7 +8,7 @@
"masksAndZones": "Mask and Zone Editor - Frigate", "masksAndZones": "Mask and Zone Editor - Frigate",
"motionTuner": "Motion Tuner - Frigate", "motionTuner": "Motion Tuner - Frigate",
"object": "Debug - Frigate", "object": "Debug - Frigate",
"general": "General Settings - Frigate", "general": "UI Settings - Frigate",
"frigatePlus": "Frigate+ Settings - Frigate", "frigatePlus": "Frigate+ Settings - Frigate",
"notifications": "Notification Settings - Frigate" "notifications": "Notification Settings - Frigate"
}, },
@ -37,7 +37,7 @@
"noCamera": "No Camera" "noCamera": "No Camera"
}, },
"general": { "general": {
"title": "General Settings", "title": "UI Settings",
"liveDashboard": { "liveDashboard": {
"title": "Live Dashboard", "title": "Live Dashboard",
"automaticLiveView": { "automaticLiveView": {
@ -51,6 +51,10 @@
"displayCameraNames": { "displayCameraNames": {
"label": "Always Show Camera Names", "label": "Always Show Camera Names",
"desc": "Always show the camera names in a chip in the multi-camera live view dashboard." "desc": "Always show the camera names in a chip in the multi-camera live view dashboard."
},
"liveFallbackTimeout": {
"label": "Live Player Fallback Timeout",
"desc": "When a camera's high quality live stream is unavailable, fall back to low bandwidth mode after this many seconds. Default: 3."
} }
}, },
"storedLayouts": { "storedLayouts": {

View File

@ -1,4 +1,5 @@
import { baseUrl } from "@/api/baseUrl"; import { baseUrl } from "@/api/baseUrl";
import { usePersistence } from "@/hooks/use-persistence";
import { import {
LivePlayerError, LivePlayerError,
PlayerStatsType, PlayerStatsType,
@ -71,6 +72,8 @@ function MSEPlayer({
const [errorCount, setErrorCount] = useState<number>(0); const [errorCount, setErrorCount] = useState<number>(0);
const totalBytesLoaded = useRef(0); const totalBytesLoaded = useRef(0);
const [fallbackTimeout] = usePersistence<number>("liveFallbackTimeout", 3);
const videoRef = useRef<HTMLVideoElement>(null); const videoRef = useRef<HTMLVideoElement>(null);
const wsRef = useRef<WebSocket | null>(null); const wsRef = useRef<WebSocket | null>(null);
const reconnectTIDRef = useRef<number | null>(null); const reconnectTIDRef = useRef<number | null>(null);
@ -475,7 +478,10 @@ function MSEPlayer({
setBufferTimeout(undefined); setBufferTimeout(undefined);
} }
const timeoutDuration = bufferTime == 0 ? 5000 : 3000; const timeoutDuration =
bufferTime == 0
? (fallbackTimeout ?? 3) * 2 * 1000
: (fallbackTimeout ?? 3) * 1000;
setBufferTimeout( setBufferTimeout(
setTimeout(() => { setTimeout(() => {
if ( if (
@ -500,6 +506,7 @@ function MSEPlayer({
onError, onError,
onPlaying, onPlaying,
playbackEnabled, playbackEnabled,
fallbackTimeout,
]); ]);
useEffect(() => { useEffect(() => {

View File

@ -99,6 +99,10 @@ export default function UiSettingsView() {
const [playbackRate, setPlaybackRate] = usePersistence("playbackRate", 1); const [playbackRate, setPlaybackRate] = usePersistence("playbackRate", 1);
const [weekStartsOn, setWeekStartsOn] = usePersistence("weekStartsOn", 0); const [weekStartsOn, setWeekStartsOn] = usePersistence("weekStartsOn", 0);
const [alertVideos, setAlertVideos] = usePersistence("alertVideos", true); const [alertVideos, setAlertVideos] = usePersistence("alertVideos", true);
const [fallbackTimeout, setFallbackTimeout] = usePersistence(
"liveFallbackTimeout",
3,
);
return ( return (
<> <>
@ -161,6 +165,48 @@ export default function UiSettingsView() {
<p>{t("general.liveDashboard.displayCameraNames.desc")}</p> <p>{t("general.liveDashboard.displayCameraNames.desc")}</p>
</div> </div>
</div> </div>
<div className="space-y-3">
<div className="flex flex-row items-center justify-start gap-2">
<Label
className="cursor-pointer"
htmlFor="live-fallback-timeout"
>
{t("general.liveDashboard.liveFallbackTimeout.label")}
</Label>
</div>
<div className="my-2 max-w-5xl text-sm text-muted-foreground">
<p>{t("general.liveDashboard.liveFallbackTimeout.desc")}</p>
</div>
<Select
value={fallbackTimeout?.toString()}
onValueChange={(value) => setFallbackTimeout(parseInt(value))}
>
<SelectTrigger className="w-36">
{t("time.second", {
ns: "common",
time: fallbackTimeout,
count: fallbackTimeout,
})}
</SelectTrigger>
<SelectContent>
<SelectGroup>
{[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((timeout) => (
<SelectItem
key={timeout}
className="cursor-pointer"
value={timeout.toString()}
>
{t("time.second", {
ns: "common",
time: timeout,
count: timeout,
})}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
</div>
</div> </div>
<div className="my-3 flex w-full flex-col space-y-6"> <div className="my-3 flex w-full flex-col space-y-6">