Misc Improvements (#14076)

* Return ID of export in http response

* Ignore keyboard listener when typing in text field

* Add other keyboard listeners
This commit is contained in:
Nicolas Mowen 2024-09-30 15:55:44 -06:00 committed by GitHub
parent 95d6da3111
commit 94fd75e014
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 46 additions and 7 deletions

View File

@ -1,6 +1,8 @@
"""Export apis.""" """Export apis."""
import logging import logging
import random
import string
from pathlib import Path from pathlib import Path
from typing import Optional from typing import Optional
@ -72,8 +74,10 @@ def export_recording(
status_code=400, status_code=400,
) )
export_id = f"{camera_name}_{''.join(random.choices(string.ascii_lowercase + string.digits, k=6))}"
exporter = RecordingExporter( exporter = RecordingExporter(
request.app.frigate_config, request.app.frigate_config,
export_id,
camera_name, camera_name,
friendly_name, friendly_name,
existing_image, existing_image,
@ -91,6 +95,7 @@ def export_recording(
{ {
"success": True, "success": True,
"message": "Starting export of recording.", "message": "Starting export of recording.",
"export_id": export_id,
} }
), ),
status_code=200, status_code=200,

View File

@ -49,6 +49,7 @@ class RecordingExporter(threading.Thread):
def __init__( def __init__(
self, self,
config: FrigateConfig, config: FrigateConfig,
id: str,
camera: str, camera: str,
name: Optional[str], name: Optional[str],
image: Optional[str], image: Optional[str],
@ -58,6 +59,7 @@ class RecordingExporter(threading.Thread):
) -> None: ) -> None:
threading.Thread.__init__(self) threading.Thread.__init__(self)
self.config = config self.config = config
self.export_id = id
self.camera = camera self.camera = camera
self.user_provided_name = name self.user_provided_name = name
self.user_provided_image = image self.user_provided_image = image
@ -172,18 +174,17 @@ class RecordingExporter(threading.Thread):
logger.debug( logger.debug(
f"Beginning export for {self.camera} from {self.start_time} to {self.end_time}" f"Beginning export for {self.camera} from {self.start_time} to {self.end_time}"
) )
export_id = f"{self.camera}_{''.join(random.choices(string.ascii_lowercase + string.digits, k=6))}"
export_name = ( export_name = (
self.user_provided_name self.user_provided_name
or f"{self.camera.replace('_', ' ')} {self.get_datetime_from_timestamp(self.start_time)} {self.get_datetime_from_timestamp(self.end_time)}" or f"{self.camera.replace('_', ' ')} {self.get_datetime_from_timestamp(self.start_time)} {self.get_datetime_from_timestamp(self.end_time)}"
) )
video_path = f"{EXPORT_DIR}/{export_id}.mp4" video_path = f"{EXPORT_DIR}/{self.export_id}.mp4"
thumb_path = self.save_thumbnail(export_id) thumb_path = self.save_thumbnail(self.export_id)
Export.insert( Export.insert(
{ {
Export.id: export_id, Export.id: self.export_id,
Export.camera: self.camera, Export.camera: self.camera,
Export.name: export_name, Export.name: export_name,
Export.date: self.start_time, Export.date: self.start_time,
@ -257,12 +258,12 @@ class RecordingExporter(threading.Thread):
) )
logger.error(p.stderr) logger.error(p.stderr)
Path(video_path).unlink(missing_ok=True) Path(video_path).unlink(missing_ok=True)
Export.delete().where(Export.id == export_id).execute() Export.delete().where(Export.id == self.export_id).execute()
Path(thumb_path).unlink(missing_ok=True) Path(thumb_path).unlink(missing_ok=True)
return return
else: else:
Export.update({Export.in_progress: False}).where( Export.update({Export.in_progress: False}).where(
Export.id == export_id Export.id == self.export_id
).execute() ).execute()
logger.debug(f"Finished exporting {video_path}") logger.debug(f"Finished exporting {video_path}")

View File

@ -14,7 +14,8 @@ export default function useKeyboardListener(
) { ) {
const keyDownListener = useCallback( const keyDownListener = useCallback(
(e: KeyboardEvent) => { (e: KeyboardEvent) => {
if (!e) { // @ts-expect-error we know this field exists
if (!e || e.target.tagName == "INPUT") {
return; return;
} }

View File

@ -1,4 +1,5 @@
import { useFullscreen } from "@/hooks/use-fullscreen"; import { useFullscreen } from "@/hooks/use-fullscreen";
import useKeyboardListener from "@/hooks/use-keyboard-listener";
import { import {
useHashState, useHashState,
usePersistedOverlayState, usePersistedOverlayState,
@ -43,6 +44,18 @@ function Live() {
const { fullscreen, toggleFullscreen, supportsFullScreen } = const { fullscreen, toggleFullscreen, supportsFullScreen } =
useFullscreen(mainRef); useFullscreen(mainRef);
useKeyboardListener(["f"], (key, modifiers) => {
if (!modifiers.down) {
return;
}
switch (key) {
case "f":
toggleFullscreen();
break;
}
});
// document title // document title
useEffect(() => { useEffect(() => {

View File

@ -236,6 +236,25 @@ export default function LiveCameraView({
return "mse"; return "mse";
}, [lowBandwidth, mic, webRTC, isRestreamed]); }, [lowBandwidth, mic, webRTC, isRestreamed]);
useKeyboardListener(["m"], (key, modifiers) => {
if (!modifiers.down) {
return;
}
switch (key) {
case "m":
if (supportsAudioOutput) {
setAudio(!audio);
}
break;
case "t":
if (supports2WayTalk) {
setMic(!mic);
}
break;
}
});
// layout state // layout state
const windowAspectRatio = useMemo(() => { const windowAspectRatio = useMemo(() => {