mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-11-01 19:30:33 +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,
|
||||
)
|
||||
|
||||
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)
|
||||
seconds = int(diff % 60)
|
||||
config: FrigateConfig = request.app.frigate_config
|
||||
@@ -1542,7 +1543,7 @@ async def preview_gif(
|
||||
"-ss",
|
||||
f"00:{minutes}:{seconds}",
|
||||
"-t",
|
||||
f"{end_ts - start_ts}",
|
||||
f"{end_ts - clip_start}",
|
||||
"-i",
|
||||
preview.path,
|
||||
"-r",
|
||||
@@ -1721,7 +1722,8 @@ async def preview_mp4(
|
||||
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)
|
||||
seconds = int(diff % 60)
|
||||
config: FrigateConfig = request.app.frigate_config
|
||||
@@ -1734,7 +1736,7 @@ async def preview_mp4(
|
||||
"-ss",
|
||||
f"00:{minutes}:{seconds}",
|
||||
"-t",
|
||||
f"{end_ts - start_ts}",
|
||||
f"{end_ts - clip_start}",
|
||||
"-i",
|
||||
preview.path,
|
||||
"-r",
|
||||
|
||||
@@ -163,8 +163,7 @@ async def review(
|
||||
|
||||
# Apply ordering and limit
|
||||
review_query = (
|
||||
review_query.order_by(ReviewSegment.severity.asc())
|
||||
.order_by(ReviewSegment.start_time.desc())
|
||||
review_query.order_by(ReviewSegment.start_time.desc())
|
||||
.limit(limit)
|
||||
.dicts()
|
||||
.iterator()
|
||||
|
||||
@@ -1470,6 +1470,9 @@ class RecordingExporter(threading.Thread):
|
||||
|
||||
try:
|
||||
self._run_export(video_path, thumb_path)
|
||||
except Exception:
|
||||
self._discard_failed_export(video_path, thumb_path)
|
||||
raise
|
||||
finally:
|
||||
# staged runs hold a full copy of the export, so they must not
|
||||
# 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."""
|
||||
Path(video_path).unlink(missing_ok=True)
|
||||
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:
|
||||
try:
|
||||
@@ -1498,6 +1502,7 @@ class RecordingExporter(threading.Thread):
|
||||
else:
|
||||
ffmpeg_cmd, playlist_lines = self.get_preview_export_command(video_path)
|
||||
except DoesNotExist:
|
||||
self._discard_failed_export(video_path, thumb_path)
|
||||
return
|
||||
|
||||
# When neither custom ffmpeg arg is set the default path uses
|
||||
|
||||
@@ -415,6 +415,7 @@ export default function NotificationsSettingsExtras({
|
||||
pushSubscription.unsubscribe();
|
||||
workerRegistration.unregister();
|
||||
setRegistration(null);
|
||||
removeMessage("notification_settings", "registration");
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
@@ -423,9 +424,10 @@ export default function NotificationsSettingsExtras({
|
||||
});
|
||||
workerRegistration.unregister();
|
||||
setRegistration(null);
|
||||
removeMessage("notification_settings", "registration");
|
||||
});
|
||||
},
|
||||
[addMessage, publicKey, t],
|
||||
[addMessage, removeMessage, publicKey, t],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -794,7 +794,7 @@ export default function DraggableGridLayout({
|
||||
toggleAudio={() => toggleAudio(camera.name)}
|
||||
statsState={statsStates[camera.name]}
|
||||
toggleStats={() => toggleStats(camera.name)}
|
||||
volumeState={volumeStates[camera.name]}
|
||||
volumeState={volumeStates[camera.name] ?? 1}
|
||||
setVolumeState={(value) =>
|
||||
setVolumeStates((prev) => ({
|
||||
...prev,
|
||||
@@ -839,7 +839,7 @@ export default function DraggableGridLayout({
|
||||
onError={(e) => handleError(camera.name, e)}
|
||||
onResetLiveMode={() => resetPreferredLiveMode(camera.name)}
|
||||
playAudio={audioStates[camera.name]}
|
||||
volume={volumeStates[camera.name]}
|
||||
volume={volumeStates[camera.name] ?? 1}
|
||||
onLiveAspectChange={liveAspectHandlers[camera.name]}
|
||||
/>
|
||||
</GridLiveContextMenu>
|
||||
|
||||
@@ -629,7 +629,7 @@ export default function LiveDashboardView({
|
||||
resetPreferredLiveMode(camera.name)
|
||||
}
|
||||
playAudio={audioStates[camera.name] ?? false}
|
||||
volume={volumeStates[camera.name]}
|
||||
volume={volumeStates[camera.name] ?? 1}
|
||||
/>
|
||||
</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 { toast } from "sonner";
|
||||
import useSWR from "swr";
|
||||
@@ -133,6 +133,9 @@ export default function TriggerView({
|
||||
|
||||
// watch websocket for updates
|
||||
const { payload: triggers_status_ws } = useTriggers();
|
||||
const highlightTimers = useRef<Record<string, ReturnType<typeof setTimeout>>>(
|
||||
{},
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!triggers_status_ws) return;
|
||||
@@ -148,11 +151,11 @@ export default function TriggerView({
|
||||
return current;
|
||||
});
|
||||
|
||||
// not cleared on the next payload, or a second trigger firing within
|
||||
// 3s would leave this one highlighted
|
||||
setTimeout(() => {
|
||||
const name = triggers_status_ws.name;
|
||||
clearTimeout(highlightTimers.current[name]);
|
||||
highlightTimers.current[name] = setTimeout(() => {
|
||||
setTriggeredTrigger((prev) =>
|
||||
(prev || []).filter((name) => name !== triggers_status_ws.name),
|
||||
(prev || []).filter((triggered) => triggered !== name),
|
||||
);
|
||||
}, 3000);
|
||||
}, [triggers_status_ws, selectedCamera, mutate]);
|
||||
|
||||
Reference in New Issue
Block a user