mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-11-03 20:30:30 +03:00
address review feedback
- Discard the export row when an export fails before or during ffmpeg, so it doesn't stay in progress until a restart - Skip the empty thumbnail path when discarding a failed export - Measure preview clip duration from the clamped start - Default live player volume to 1 when the group has no saved setting - Clear the unsaved registration message when notification registration fails - Keep a highlight timer per trigger - Remove the severity order_by in /review, which the start_time order_by replaced
This commit is contained in:
@@ -1530,7 +1530,8 @@ async def preview_gif(
|
|||||||
status_code=404,
|
status_code=404,
|
||||||
)
|
)
|
||||||
|
|
||||||
diff = max(start_ts - preview.start_time, 0)
|
clip_start = max(start_ts, preview.start_time)
|
||||||
|
diff = clip_start - preview.start_time
|
||||||
minutes = int(diff / 60)
|
minutes = int(diff / 60)
|
||||||
seconds = int(diff % 60)
|
seconds = int(diff % 60)
|
||||||
config: FrigateConfig = request.app.frigate_config
|
config: FrigateConfig = request.app.frigate_config
|
||||||
@@ -1542,7 +1543,7 @@ async def preview_gif(
|
|||||||
"-ss",
|
"-ss",
|
||||||
f"00:{minutes}:{seconds}",
|
f"00:{minutes}:{seconds}",
|
||||||
"-t",
|
"-t",
|
||||||
f"{end_ts - start_ts}",
|
f"{end_ts - clip_start}",
|
||||||
"-i",
|
"-i",
|
||||||
preview.path,
|
preview.path,
|
||||||
"-r",
|
"-r",
|
||||||
@@ -1721,7 +1722,8 @@ async def preview_mp4(
|
|||||||
status_code=404,
|
status_code=404,
|
||||||
)
|
)
|
||||||
|
|
||||||
diff = max(start_ts - preview.start_time, 0)
|
clip_start = max(start_ts, preview.start_time)
|
||||||
|
diff = clip_start - preview.start_time
|
||||||
minutes = int(diff / 60)
|
minutes = int(diff / 60)
|
||||||
seconds = int(diff % 60)
|
seconds = int(diff % 60)
|
||||||
config: FrigateConfig = request.app.frigate_config
|
config: FrigateConfig = request.app.frigate_config
|
||||||
@@ -1734,7 +1736,7 @@ async def preview_mp4(
|
|||||||
"-ss",
|
"-ss",
|
||||||
f"00:{minutes}:{seconds}",
|
f"00:{minutes}:{seconds}",
|
||||||
"-t",
|
"-t",
|
||||||
f"{end_ts - start_ts}",
|
f"{end_ts - clip_start}",
|
||||||
"-i",
|
"-i",
|
||||||
preview.path,
|
preview.path,
|
||||||
"-r",
|
"-r",
|
||||||
|
|||||||
@@ -163,8 +163,7 @@ async def review(
|
|||||||
|
|
||||||
# Apply ordering and limit
|
# Apply ordering and limit
|
||||||
review_query = (
|
review_query = (
|
||||||
review_query.order_by(ReviewSegment.severity.asc())
|
review_query.order_by(ReviewSegment.start_time.desc())
|
||||||
.order_by(ReviewSegment.start_time.desc())
|
|
||||||
.limit(limit)
|
.limit(limit)
|
||||||
.dicts()
|
.dicts()
|
||||||
.iterator()
|
.iterator()
|
||||||
|
|||||||
@@ -1470,6 +1470,9 @@ class RecordingExporter(threading.Thread):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
self._run_export(video_path, thumb_path)
|
self._run_export(video_path, thumb_path)
|
||||||
|
except Exception:
|
||||||
|
self._discard_failed_export(video_path, thumb_path)
|
||||||
|
raise
|
||||||
finally:
|
finally:
|
||||||
# staged runs hold a full copy of the export, so they must not
|
# staged runs hold a full copy of the export, so they must not
|
||||||
# survive a failure the way the small chapter file could
|
# survive a failure the way the small chapter file could
|
||||||
@@ -1479,7 +1482,8 @@ class RecordingExporter(threading.Thread):
|
|||||||
"""Drop the partial output and the row that promised it."""
|
"""Drop the partial output and the row that promised it."""
|
||||||
Path(video_path).unlink(missing_ok=True)
|
Path(video_path).unlink(missing_ok=True)
|
||||||
Export.delete().where(Export.id == self.export_id).execute()
|
Export.delete().where(Export.id == self.export_id).execute()
|
||||||
Path(thumb_path).unlink(missing_ok=True)
|
if thumb_path:
|
||||||
|
Path(thumb_path).unlink(missing_ok=True)
|
||||||
|
|
||||||
def _run_export(self, video_path: str, thumb_path: str) -> None:
|
def _run_export(self, video_path: str, thumb_path: str) -> None:
|
||||||
try:
|
try:
|
||||||
@@ -1498,6 +1502,7 @@ class RecordingExporter(threading.Thread):
|
|||||||
else:
|
else:
|
||||||
ffmpeg_cmd, playlist_lines = self.get_preview_export_command(video_path)
|
ffmpeg_cmd, playlist_lines = self.get_preview_export_command(video_path)
|
||||||
except DoesNotExist:
|
except DoesNotExist:
|
||||||
|
self._discard_failed_export(video_path, thumb_path)
|
||||||
return
|
return
|
||||||
|
|
||||||
# When neither custom ffmpeg arg is set the default path uses
|
# When neither custom ffmpeg arg is set the default path uses
|
||||||
|
|||||||
@@ -415,6 +415,7 @@ export default function NotificationsSettingsExtras({
|
|||||||
pushSubscription.unsubscribe();
|
pushSubscription.unsubscribe();
|
||||||
workerRegistration.unregister();
|
workerRegistration.unregister();
|
||||||
setRegistration(null);
|
setRegistration(null);
|
||||||
|
removeMessage("notification_settings", "registration");
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
@@ -423,9 +424,10 @@ export default function NotificationsSettingsExtras({
|
|||||||
});
|
});
|
||||||
workerRegistration.unregister();
|
workerRegistration.unregister();
|
||||||
setRegistration(null);
|
setRegistration(null);
|
||||||
|
removeMessage("notification_settings", "registration");
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[addMessage, publicKey, t],
|
[addMessage, removeMessage, publicKey, t],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -794,7 +794,7 @@ export default function DraggableGridLayout({
|
|||||||
toggleAudio={() => toggleAudio(camera.name)}
|
toggleAudio={() => toggleAudio(camera.name)}
|
||||||
statsState={statsStates[camera.name]}
|
statsState={statsStates[camera.name]}
|
||||||
toggleStats={() => toggleStats(camera.name)}
|
toggleStats={() => toggleStats(camera.name)}
|
||||||
volumeState={volumeStates[camera.name]}
|
volumeState={volumeStates[camera.name] ?? 1}
|
||||||
setVolumeState={(value) =>
|
setVolumeState={(value) =>
|
||||||
setVolumeStates((prev) => ({
|
setVolumeStates((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
@@ -839,7 +839,7 @@ export default function DraggableGridLayout({
|
|||||||
onError={(e) => handleError(camera.name, e)}
|
onError={(e) => handleError(camera.name, e)}
|
||||||
onResetLiveMode={() => resetPreferredLiveMode(camera.name)}
|
onResetLiveMode={() => resetPreferredLiveMode(camera.name)}
|
||||||
playAudio={audioStates[camera.name]}
|
playAudio={audioStates[camera.name]}
|
||||||
volume={volumeStates[camera.name]}
|
volume={volumeStates[camera.name] ?? 1}
|
||||||
onLiveAspectChange={liveAspectHandlers[camera.name]}
|
onLiveAspectChange={liveAspectHandlers[camera.name]}
|
||||||
/>
|
/>
|
||||||
</GridLiveContextMenu>
|
</GridLiveContextMenu>
|
||||||
|
|||||||
@@ -629,7 +629,7 @@ export default function LiveDashboardView({
|
|||||||
resetPreferredLiveMode(camera.name)
|
resetPreferredLiveMode(camera.name)
|
||||||
}
|
}
|
||||||
playAudio={audioStates[camera.name] ?? false}
|
playAudio={audioStates[camera.name] ?? false}
|
||||||
volume={volumeStates[camera.name]}
|
volume={volumeStates[camera.name] ?? 1}
|
||||||
/>
|
/>
|
||||||
</LiveContextMenu>
|
</LiveContextMenu>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
@@ -133,6 +133,9 @@ export default function TriggerView({
|
|||||||
|
|
||||||
// watch websocket for updates
|
// watch websocket for updates
|
||||||
const { payload: triggers_status_ws } = useTriggers();
|
const { payload: triggers_status_ws } = useTriggers();
|
||||||
|
const highlightTimers = useRef<Record<string, ReturnType<typeof setTimeout>>>(
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!triggers_status_ws) return;
|
if (!triggers_status_ws) return;
|
||||||
@@ -148,11 +151,11 @@ export default function TriggerView({
|
|||||||
return current;
|
return current;
|
||||||
});
|
});
|
||||||
|
|
||||||
// not cleared on the next payload, or a second trigger firing within
|
const name = triggers_status_ws.name;
|
||||||
// 3s would leave this one highlighted
|
clearTimeout(highlightTimers.current[name]);
|
||||||
setTimeout(() => {
|
highlightTimers.current[name] = setTimeout(() => {
|
||||||
setTriggeredTrigger((prev) =>
|
setTriggeredTrigger((prev) =>
|
||||||
(prev || []).filter((name) => name !== triggers_status_ws.name),
|
(prev || []).filter((triggered) => triggered !== name),
|
||||||
);
|
);
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}, [triggers_status_ws, selectedCamera, mutate]);
|
}, [triggers_status_ws, selectedCamera, mutate]);
|
||||||
|
|||||||
Reference in New Issue
Block a user