+
+
+
+
+ {t("streaming.restreaming.disabled", {
ns: "components/dialog",
})}
-
-
- {t("readTheDocumentation", { ns: "common" })}
-
-
-
-
-
-
-
- )}
- {isRestreamed &&
- Object.values(camera.live.streams).length > 0 && (
-
-
-
-
- {debug && (
-
- <>
-
-
{t("stream.debug.picker")}
- >
- )}
-
- {preferredLiveMode != "jsmpeg" &&
- !debug &&
- isRestreamed && (
-
- {supportsAudioOutput ? (
- <>
-
-
{t("stream.audio.available")}
- >
- ) : (
- <>
-
-
{t("stream.audio.unavailable")}
-
-
-
-
-
- {t("button.info", { ns: "common" })}
-
-
-
-
- {t("stream.audio.tips.title")}
-
-
- {t("readTheDocumentation", {
- ns: "common",
- })}
-
-
-
-
-
- >
- )}
-
- )}
- {preferredLiveMode != "jsmpeg" &&
- !debug &&
- isRestreamed &&
- supportsAudioOutput && (
-
- {supports2WayTalk ? (
- <>
-
-
{t("stream.twoWayTalk.available")}
- >
- ) : (
- <>
-
-
{t("stream.twoWayTalk.unavailable")}
-
-
-
-
-
- {t("button.info", { ns: "common" })}
-
-
-
-
- {t("stream.twoWayTalk.tips")}
-
-
- {t("readTheDocumentation", {
- ns: "common",
- })}
-
-
-
-
-
- >
- )}
-
- )}
-
- {preferredLiveMode == "jsmpeg" &&
- !debug &&
- isRestreamed && (
-
-
-
-
-
- {t("stream.lowBandwidth.tips")}
-
+
+
+
+
+
+ {t("button.info", { ns: "common" })}
+
-
-
- )}
+
+
+ {t("streaming.restreaming.desc.title", {
+ ns: "components/dialog",
+ })}
+
+
+ {t("readTheDocumentation", { ns: "common" })}
+
+
+
+
+
+
+
+ )}
+ {isRestreamed &&
+ Object.values(camera.live.streams).length > 0 && (
+
+
+
+
+ {debug && (
+
+ <>
+
+
{t("stream.debug.picker")}
+ >
+
+ )}
+
+ {preferredLiveMode != "jsmpeg" &&
+ !debug &&
+ isRestreamed && (
+
+ {supportsAudioOutput ? (
+ <>
+
+
{t("stream.audio.available")}
+ >
+ ) : (
+ <>
+
+
{t("stream.audio.unavailable")}
+
+
+
+
+
+ {t("button.info", { ns: "common" })}
+
+
+
+
+ {t("stream.audio.tips.title")}
+
+
+ {t("readTheDocumentation", {
+ ns: "common",
+ })}
+
+
+
+
+
+ >
+ )}
+
+ )}
+ {preferredLiveMode != "jsmpeg" &&
+ !debug &&
+ isRestreamed &&
+ supportsAudioOutput && (
+
+ {supports2WayTalk ? (
+ <>
+
+
{t("stream.twoWayTalk.available")}
+ >
+ ) : (
+ <>
+
+
{t("stream.twoWayTalk.unavailable")}
+
+
+
+
+
+ {t("button.info", { ns: "common" })}
+
+
+
+
+ {t("stream.twoWayTalk.tips")}
+
+
+ {t("readTheDocumentation", {
+ ns: "common",
+ })}
+
+
+
+
+
+ >
+ )}
+
+ )}
+
+ {preferredLiveMode == "jsmpeg" &&
+ !debug &&
+ isRestreamed && (
+
+
+
+
+
+ {t("stream.lowBandwidth.tips")}
+
+
+
+
+ )}
+
+ )}
+ {isRestreamed && (
+
+
+
+
+ setPlayInBackground(checked)
+ }
+ />
+
+
+ {t("stream.playInBackground.tips")}
+
)}
- {isRestreamed && (
- setPlayInBackground(checked)
- }
+ checked={showStats}
+ onCheckedChange={(checked) => setShowStats(checked)}
/>
- {t("stream.playInBackground.tips")}
+ {t("streaming.showStats.desc", {
+ ns: "components/dialog",
+ })}
- )}
-
-
-
- setShowStats(checked)}
- />
-
-
- {t("streaming.showStats.desc", {
- ns: "components/dialog",
- })}
-
-
-
-
-
-
setDebug(checked)}
- />
+
+
+
+ setDebug(checked)}
+ />
+
-
-
-
+
+
+ )}
>
);
}
diff --git a/web/src/views/live/LiveDashboardView.tsx b/web/src/views/live/LiveDashboardView.tsx
index 8b7dd1336..c4104576c 100644
--- a/web/src/views/live/LiveDashboardView.tsx
+++ b/web/src/views/live/LiveDashboardView.tsx
@@ -202,14 +202,6 @@ export default function LiveDashboardView({
};
}, []);
- const {
- preferredLiveModes,
- setPreferredLiveModes,
- resetPreferredLiveMode,
- isRestreamedStates,
- supportsAudioOutputStates,
- } = useCameraLiveMode(cameras, windowVisible);
-
const [globalAutoLive] = usePersistence("autoLiveView", true);
const [displayCameraNames] = usePersistence("displayCameraNames", false);
@@ -239,6 +231,33 @@ export default function LiveDashboardView({
[visibleCameraObserver.current],
);
+ const activeStreams = useMemo(() => {
+ const streams: { [cameraName: string]: string } = {};
+ cameras.forEach((camera) => {
+ const availableStreams = camera.live.streams || {};
+ const streamNameFromSettings =
+ currentGroupStreamingSettings?.[camera.name]?.streamName || "";
+ const streamExists =
+ streamNameFromSettings &&
+ Object.values(availableStreams).includes(streamNameFromSettings);
+
+ const streamName = streamExists
+ ? streamNameFromSettings
+ : Object.values(availableStreams)[0] || "";
+
+ streams[camera.name] = streamName;
+ });
+ return streams;
+ }, [cameras, currentGroupStreamingSettings]);
+
+ const {
+ preferredLiveModes,
+ setPreferredLiveModes,
+ resetPreferredLiveMode,
+ isRestreamedStates,
+ supportsAudioOutputStates,
+ } = useCameraLiveMode(cameras, windowVisible, activeStreams);
+
const birdseyeConfig = useMemo(() => config?.birdseye, [config]);
const handleError = useCallback(
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")}