From 0b43c15602a2151d4bffb1a5baa6e11df102e869 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Mon, 10 Nov 2025 18:19:40 -0600 Subject: [PATCH] add UI setting to configure jsmpeg fallback timeout --- web/public/locales/en/views/settings.json | 8 +++- web/src/components/player/MsePlayer.tsx | 9 ++++- web/src/views/settings/UiSettingsView.tsx | 46 +++++++++++++++++++++++ 3 files changed, 60 insertions(+), 3 deletions(-) diff --git a/web/public/locales/en/views/settings.json b/web/public/locales/en/views/settings.json index 626e5385a..6a077f707 100644 --- a/web/public/locales/en/views/settings.json +++ b/web/public/locales/en/views/settings.json @@ -8,7 +8,7 @@ "masksAndZones": "Mask and Zone Editor - Frigate", "motionTuner": "Motion Tuner - Frigate", "object": "Debug - Frigate", - "general": "General Settings - Frigate", + "general": "UI Settings - Frigate", "frigatePlus": "Frigate+ Settings - Frigate", "notifications": "Notification Settings - Frigate" }, @@ -37,7 +37,7 @@ "noCamera": "No Camera" }, "general": { - "title": "General Settings", + "title": "UI Settings", "liveDashboard": { "title": "Live Dashboard", "automaticLiveView": { @@ -51,6 +51,10 @@ "displayCameraNames": { "label": "Always Show Camera Names", "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": { diff --git a/web/src/components/player/MsePlayer.tsx b/web/src/components/player/MsePlayer.tsx index c4a30c7e6..4d2b8b365 100644 --- a/web/src/components/player/MsePlayer.tsx +++ b/web/src/components/player/MsePlayer.tsx @@ -1,4 +1,5 @@ import { baseUrl } from "@/api/baseUrl"; +import { usePersistence } from "@/hooks/use-persistence"; import { LivePlayerError, PlayerStatsType, @@ -71,6 +72,8 @@ function MSEPlayer({ const [errorCount, setErrorCount] = useState(0); const totalBytesLoaded = useRef(0); + const [fallbackTimeout] = usePersistence("liveFallbackTimeout", 3); + const videoRef = useRef(null); const wsRef = useRef(null); const reconnectTIDRef = useRef(null); @@ -475,7 +478,10 @@ function MSEPlayer({ setBufferTimeout(undefined); } - const timeoutDuration = bufferTime == 0 ? 5000 : 3000; + const timeoutDuration = + bufferTime == 0 + ? (fallbackTimeout ?? 3) * 2 * 1000 + : (fallbackTimeout ?? 3) * 1000; setBufferTimeout( setTimeout(() => { if ( @@ -500,6 +506,7 @@ function MSEPlayer({ onError, onPlaying, playbackEnabled, + fallbackTimeout, ]); useEffect(() => { diff --git a/web/src/views/settings/UiSettingsView.tsx b/web/src/views/settings/UiSettingsView.tsx index 092e3713d..d09ab7c75 100644 --- a/web/src/views/settings/UiSettingsView.tsx +++ b/web/src/views/settings/UiSettingsView.tsx @@ -99,6 +99,10 @@ export default function UiSettingsView() { const [playbackRate, setPlaybackRate] = usePersistence("playbackRate", 1); const [weekStartsOn, setWeekStartsOn] = usePersistence("weekStartsOn", 0); const [alertVideos, setAlertVideos] = usePersistence("alertVideos", true); + const [fallbackTimeout, setFallbackTimeout] = usePersistence( + "liveFallbackTimeout", + 3, + ); return ( <> @@ -161,6 +165,48 @@ export default function UiSettingsView() {

{t("general.liveDashboard.displayCameraNames.desc")}

+
+
+ +
+
+

{t("general.liveDashboard.liveFallbackTimeout.desc")}

+
+ +