mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-15 15:45:27 +03:00
Add option to export Timelapses from History view
This commit is contained in:
parent
77ec86d31a
commit
e50a1eb905
@ -154,7 +154,7 @@ Footage can be exported from Frigate by right-clicking (desktop) or long pressin
|
|||||||
|
|
||||||
### Time-lapse export
|
### Time-lapse export
|
||||||
|
|
||||||
Time lapse exporting is available only via the [HTTP API](../integrations/api/export-recording-export-camera-name-start-start-time-end-end-time-post.api.mdx).
|
Time lapse exporting is available while exporting from the History view, or via the [HTTP API](../integrations/api/export-recording-export-camera-name-start-start-time-end-end-time-post.api.mdx).
|
||||||
|
|
||||||
When exporting a time-lapse the default speed-up is 25x with 30 FPS. This means that every 25 seconds of (real-time) recording is condensed into 1 second of time-lapse video (always without audio) with a smoothness of 30 FPS.
|
When exporting a time-lapse the default speed-up is 25x with 30 FPS. This means that every 25 seconds of (real-time) recording is condensed into 1 second of time-lapse video (always without audio) with a smoothness of 30 FPS.
|
||||||
|
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import {
|
|||||||
import { Label } from "../ui/label";
|
import { Label } from "../ui/label";
|
||||||
import { RadioGroup, RadioGroupItem } from "../ui/radio-group";
|
import { RadioGroup, RadioGroupItem } from "../ui/radio-group";
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "../ui/button";
|
||||||
|
import { Checkbox } from "../ui/checkbox";
|
||||||
import { ExportMode } from "@/types/filter";
|
import { ExportMode } from "@/types/filter";
|
||||||
import { FaArrowDown, FaArrowRight, FaCalendarAlt } from "react-icons/fa";
|
import { FaArrowDown, FaArrowRight, FaCalendarAlt } from "react-icons/fa";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
@ -65,6 +66,7 @@ export default function ExportDialog({
|
|||||||
setShowPreview,
|
setShowPreview,
|
||||||
}: ExportDialogProps) {
|
}: ExportDialogProps) {
|
||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
|
const [isTimelapse, setIsTimelapse] = useState(false);
|
||||||
|
|
||||||
const onStartExport = useCallback(() => {
|
const onStartExport = useCallback(() => {
|
||||||
if (!range) {
|
if (!range) {
|
||||||
@ -83,7 +85,7 @@ export default function ExportDialog({
|
|||||||
.post(
|
.post(
|
||||||
`export/${camera}/start/${Math.round(range.after)}/end/${Math.round(range.before)}`,
|
`export/${camera}/start/${Math.round(range.after)}/end/${Math.round(range.before)}`,
|
||||||
{
|
{
|
||||||
playback: "realtime",
|
playback: isTimelapse ? "timelapse_25x" : "realtime",
|
||||||
name,
|
name,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -96,6 +98,7 @@ export default function ExportDialog({
|
|||||||
setName("");
|
setName("");
|
||||||
setRange(undefined);
|
setRange(undefined);
|
||||||
setMode("none");
|
setMode("none");
|
||||||
|
setIsTimelapse(false);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
@ -110,7 +113,7 @@ export default function ExportDialog({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, [camera, name, range, setRange, setName, setMode]);
|
}, [camera, name, range, isTimelapse, setRange, setName, setMode]);
|
||||||
|
|
||||||
const Overlay = isDesktop ? Dialog : Drawer;
|
const Overlay = isDesktop ? Dialog : Drawer;
|
||||||
const Trigger = isDesktop ? DialogTrigger : DrawerTrigger;
|
const Trigger = isDesktop ? DialogTrigger : DrawerTrigger;
|
||||||
@ -129,13 +132,17 @@ export default function ExportDialog({
|
|||||||
show={mode == "timeline"}
|
show={mode == "timeline"}
|
||||||
onPreview={() => setShowPreview(true)}
|
onPreview={() => setShowPreview(true)}
|
||||||
onSave={() => onStartExport()}
|
onSave={() => onStartExport()}
|
||||||
onCancel={() => setMode("none")}
|
onCancel={() => {
|
||||||
|
setMode("none");
|
||||||
|
setIsTimelapse(false);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<Overlay
|
<Overlay
|
||||||
open={mode == "select"}
|
open={mode == "select"}
|
||||||
onOpenChange={(open) => {
|
onOpenChange={(open) => {
|
||||||
if (!open) {
|
if (!open) {
|
||||||
setMode("none");
|
setMode("none");
|
||||||
|
setIsTimelapse(false);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -172,11 +179,16 @@ export default function ExportDialog({
|
|||||||
currentTime={currentTime}
|
currentTime={currentTime}
|
||||||
range={range}
|
range={range}
|
||||||
name={name}
|
name={name}
|
||||||
|
isTimelapse={isTimelapse}
|
||||||
onStartExport={onStartExport}
|
onStartExport={onStartExport}
|
||||||
setName={setName}
|
setName={setName}
|
||||||
setRange={setRange}
|
setRange={setRange}
|
||||||
setMode={setMode}
|
setMode={setMode}
|
||||||
onCancel={() => setMode("none")}
|
setIsTimelapse={setIsTimelapse}
|
||||||
|
onCancel={() => {
|
||||||
|
setMode("none");
|
||||||
|
setIsTimelapse(false);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Content>
|
</Content>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
@ -189,10 +201,12 @@ type ExportContentProps = {
|
|||||||
currentTime: number;
|
currentTime: number;
|
||||||
range?: TimeRange;
|
range?: TimeRange;
|
||||||
name: string;
|
name: string;
|
||||||
|
isTimelapse: boolean;
|
||||||
onStartExport: () => void;
|
onStartExport: () => void;
|
||||||
setName: (name: string) => void;
|
setName: (name: string) => void;
|
||||||
setRange: (range: TimeRange | undefined) => void;
|
setRange: (range: TimeRange | undefined) => void;
|
||||||
setMode: (mode: ExportMode) => void;
|
setMode: (mode: ExportMode) => void;
|
||||||
|
setIsTimelapse: (isTimelapse: boolean) => void;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
};
|
};
|
||||||
export function ExportContent({
|
export function ExportContent({
|
||||||
@ -200,10 +214,12 @@ export function ExportContent({
|
|||||||
currentTime,
|
currentTime,
|
||||||
range,
|
range,
|
||||||
name,
|
name,
|
||||||
|
isTimelapse,
|
||||||
onStartExport,
|
onStartExport,
|
||||||
setName,
|
setName,
|
||||||
setRange,
|
setRange,
|
||||||
setMode,
|
setMode,
|
||||||
|
setIsTimelapse,
|
||||||
onCancel,
|
onCancel,
|
||||||
}: ExportContentProps) {
|
}: ExportContentProps) {
|
||||||
const [selectedOption, setSelectedOption] = useState<ExportOption>("1");
|
const [selectedOption, setSelectedOption] = useState<ExportOption>("1");
|
||||||
@ -289,6 +305,22 @@ export function ExportContent({
|
|||||||
setRange={setRange}
|
setRange={setRange}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{isDesktop && <SelectSeparator className="my-4 bg-secondary" />}
|
||||||
|
<div className="mt-4 flex items-center gap-2">
|
||||||
|
<Checkbox
|
||||||
|
className={
|
||||||
|
isTimelapse
|
||||||
|
? "bg-selected from-selected/50 to-selected/90 text-selected"
|
||||||
|
: "bg-secondary from-secondary/50 to-secondary/90 text-secondary"
|
||||||
|
}
|
||||||
|
id="timelapse"
|
||||||
|
checked={isTimelapse}
|
||||||
|
onCheckedChange={(checked) => setIsTimelapse(checked as boolean)}
|
||||||
|
/>
|
||||||
|
<Label htmlFor="timelapse" className="cursor-pointer capitalize">
|
||||||
|
Timelapse
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
<Input
|
<Input
|
||||||
className="text-md my-6"
|
className="text-md my-6"
|
||||||
type="search"
|
type="search"
|
||||||
@ -319,6 +351,7 @@ export function ExportContent({
|
|||||||
onStartExport();
|
onStartExport();
|
||||||
setSelectedOption("1");
|
setSelectedOption("1");
|
||||||
setMode("none");
|
setMode("none");
|
||||||
|
setIsTimelapse(false);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -62,6 +62,7 @@ export default function MobileReviewSettingsDrawer({
|
|||||||
setShowExportPreview,
|
setShowExportPreview,
|
||||||
}: MobileReviewSettingsDrawerProps) {
|
}: MobileReviewSettingsDrawerProps) {
|
||||||
const [drawerMode, setDrawerMode] = useState<DrawerMode>("none");
|
const [drawerMode, setDrawerMode] = useState<DrawerMode>("none");
|
||||||
|
const [isTimelapse, setIsTimelapse] = useState(false);
|
||||||
|
|
||||||
// exports
|
// exports
|
||||||
|
|
||||||
@ -83,7 +84,7 @@ export default function MobileReviewSettingsDrawer({
|
|||||||
.post(
|
.post(
|
||||||
`export/${camera}/start/${Math.round(range.after)}/end/${Math.round(range.before)}`,
|
`export/${camera}/start/${Math.round(range.after)}/end/${Math.round(range.before)}`,
|
||||||
{
|
{
|
||||||
playback: "realtime",
|
playback: isTimelapse ? "timelapse_25x" : "realtime",
|
||||||
name,
|
name,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -96,6 +97,7 @@ export default function MobileReviewSettingsDrawer({
|
|||||||
setName("");
|
setName("");
|
||||||
setRange(undefined);
|
setRange(undefined);
|
||||||
setMode("none");
|
setMode("none");
|
||||||
|
setIsTimelapse(false);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
@ -110,7 +112,7 @@ export default function MobileReviewSettingsDrawer({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, [camera, name, range, setRange, setName, setMode]);
|
}, [camera, name, range, isTimelapse, setRange, setName, setMode]);
|
||||||
|
|
||||||
// filters
|
// filters
|
||||||
|
|
||||||
@ -177,6 +179,7 @@ export default function MobileReviewSettingsDrawer({
|
|||||||
currentTime={currentTime}
|
currentTime={currentTime}
|
||||||
range={range}
|
range={range}
|
||||||
name={name}
|
name={name}
|
||||||
|
isTimelapse={isTimelapse}
|
||||||
onStartExport={onStartExport}
|
onStartExport={onStartExport}
|
||||||
setName={setName}
|
setName={setName}
|
||||||
setRange={setRange}
|
setRange={setRange}
|
||||||
@ -187,10 +190,12 @@ export default function MobileReviewSettingsDrawer({
|
|||||||
setDrawerMode("none");
|
setDrawerMode("none");
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
setIsTimelapse={setIsTimelapse}
|
||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
setMode("none");
|
setMode("none");
|
||||||
setRange(undefined);
|
setRange(undefined);
|
||||||
setDrawerMode("select");
|
setDrawerMode("select");
|
||||||
|
setIsTimelapse(false);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -289,7 +294,10 @@ export default function MobileReviewSettingsDrawer({
|
|||||||
className="pointer-events-none absolute left-1/2 top-8 z-50 -translate-x-1/2"
|
className="pointer-events-none absolute left-1/2 top-8 z-50 -translate-x-1/2"
|
||||||
show={mode == "timeline"}
|
show={mode == "timeline"}
|
||||||
onSave={() => onStartExport()}
|
onSave={() => onStartExport()}
|
||||||
onCancel={() => setMode("none")}
|
onCancel={() => {
|
||||||
|
setMode("none");
|
||||||
|
setIsTimelapse(false);
|
||||||
|
}}
|
||||||
onPreview={() => setShowExportPreview(true)}
|
onPreview={() => setShowExportPreview(true)}
|
||||||
/>
|
/>
|
||||||
<ExportPreviewDialog
|
<ExportPreviewDialog
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user