diff --git a/frigate/http.py b/frigate/http.py index 6a768fae0..f84a1e8bf 100644 --- a/frigate/http.py +++ b/frigate/http.py @@ -1505,7 +1505,7 @@ def vod_event(id): ) -@bp.route("/export//start//end/") +@bp.route("/export//start//end/", methods=["POST"]) def export_recording(camera_name: str, start_time: int, end_time: int): playback_factor = request.args.get("playback", type=str, default="realtime") exporter = RecordingExporter( diff --git a/frigate/record/export.py b/frigate/record/export.py index ea93e9965..d9a24eb51 100644 --- a/frigate/record/export.py +++ b/frigate/record/export.py @@ -14,7 +14,7 @@ logger = logging.getLogger(__name__) class PlaybackFactorEnum(str, Enum): - real_time = "real_time" + realtime = "realtime" timelapse_5x = "timelapse_5x" @@ -80,14 +80,14 @@ class RecordingExporter(threading.Thread): "/dev/stdin", ] - if self.playback_factor == PlaybackFactorEnum.real_time: + if self.playback_factor == PlaybackFactorEnum.realtime: ffmpeg_cmd.extend(["-c", "copy", file_name]) elif self.playback_factor == PlaybackFactorEnum.timelapse_5x: ffmpeg_cmd.extend(["-vf", "setpts=0.25*PTS", "-r", "5", "-an", file_name]) p = sp.run( ffmpeg_cmd, - input="\n".join(playlist_lines) + input="\n".join(playlist_lines), encoding="ascii", capture_output=True, ) diff --git a/web/src/Sidebar.jsx b/web/src/Sidebar.jsx index a7d7639e8..4497dba15 100644 --- a/web/src/Sidebar.jsx +++ b/web/src/Sidebar.jsx @@ -44,6 +44,7 @@ export default function Sidebar() { {birdseye?.enabled ? : null} + diff --git a/web/src/app.tsx b/web/src/app.tsx index 124a0f3cb..4e5123563 100644 --- a/web/src/app.tsx +++ b/web/src/app.tsx @@ -31,6 +31,7 @@ export default function App() { + { + if (camera == 'select') { + setMessage({ text: 'A camera needs to be selected.', error: true }); + return; + } + + if (playback == 'select') { + setMessage({ text: 'A playback factor needs to be selected.', error: true }); + return; + } + + const start = new Date(document.getElementById('start').value).getTime() / 1000; + const end = new Date(document.getElementById('end').value).getTime() / 1000; + + if (!start || !end) { + setMessage({ text: 'A start and end time needs to be selected', error: true }); + return; + } + + setMessage({ text: 'Successfully started export. View the file in the /exports folder.', error: false }); + axios.post(`export/${camera}/start/${start}/end/${end}`, { playback }); + }; + + return ( +
+ Export + + {message.text && ( +
{message.text}
+ )} + +
+ + +
+ +
+ + From: + + + + To: + + +
+ +
+ ); +} diff --git a/web/src/routes/index.js b/web/src/routes/index.js index eeadbbb4f..322a2b69a 100644 --- a/web/src/routes/index.js +++ b/web/src/routes/index.js @@ -23,6 +23,11 @@ export async function getEvents(_url, _cb, _props) { return module.default; } +export async function getExports(_url, _cb, _props) { + const module = await import('./Export.jsx'); + return module.default; +} + export async function getRecording(_url, _cb, _props) { const module = await import('./Recording.jsx'); return module.default;