Add http endpoint

This commit is contained in:
Nick Mowen 2023-05-20 09:16:29 -06:00
parent 63d95fa1e2
commit 3649dd3d59
3 changed files with 33 additions and 9 deletions

View File

@ -24,6 +24,7 @@ from frigate.const import (
CLIPS_DIR, CLIPS_DIR,
CONFIG_DIR, CONFIG_DIR,
DEFAULT_DB_PATH, DEFAULT_DB_PATH,
EXPORT_DIR,
MODEL_CACHE_DIR, MODEL_CACHE_DIR,
RECORD_DIR, RECORD_DIR,
) )
@ -68,7 +69,7 @@ class FrigateApp:
os.environ[key] = value os.environ[key] = value
def ensure_dirs(self) -> None: def ensure_dirs(self) -> None:
for d in [CONFIG_DIR, RECORD_DIR, CLIPS_DIR, CACHE_DIR, MODEL_CACHE_DIR]: for d in [CONFIG_DIR, RECORD_DIR, CLIPS_DIR, CACHE_DIR, MODEL_CACHE_DIR, EXPORT_DIR]:
if not os.path.exists(d) and not os.path.islink(d): if not os.path.exists(d) and not os.path.islink(d):
logger.info(f"Creating directory: {d}") logger.info(f"Creating directory: {d}")
os.makedirs(d) os.makedirs(d)

View File

@ -35,6 +35,7 @@ from frigate.models import Event, Recordings, Timeline
from frigate.object_processing import TrackedObject from frigate.object_processing import TrackedObject
from frigate.plus import PlusApi from frigate.plus import PlusApi
from frigate.ptz import OnvifController from frigate.ptz import OnvifController
from frigate.record.export import PlaybackFactorEnum, RecordingExporter
from frigate.stats import stats_snapshot from frigate.stats import stats_snapshot
from frigate.storage import StorageMaintainer from frigate.storage import StorageMaintainer
from frigate.util import ( from frigate.util import (
@ -1504,6 +1505,21 @@ def vod_event(id):
) )
@bp.route("/export/<camera_name>/start/<start_time>/end/<end_time>")
def export_recording(camera_name: str, start_time: int, end_time: int):
playback_factor = request.args.get("playback", type=str, default="realtime")
exporter = RecordingExporter(
camera_name,
int(start_time),
int(end_time),
PlaybackFactorEnum[playback_factor]
if playback_factor in PlaybackFactorEnum.__members__.values()
else PlaybackFactorEnum.real_time,
)
exporter.start()
return "Starting export of recording", 200
def imagestream(detected_frames_processor, camera_name, fps, height, draw_options): def imagestream(detected_frames_processor, camera_name, fps, height, draw_options):
while True: while True:
# max out at specified FPS # max out at specified FPS

View File

@ -28,6 +28,7 @@ class RecordingExporter(threading.Thread):
end_time: int, end_time: int,
playback_factor: PlaybackFactorEnum, playback_factor: PlaybackFactorEnum,
) -> None: ) -> None:
threading.Thread.__init__(self)
self.camera = camera self.camera = camera
self.start_time = start_time self.start_time = start_time
self.end_time = end_time self.end_time = end_time
@ -35,21 +36,25 @@ class RecordingExporter(threading.Thread):
def get_datetime_from_timestamp(self, timestamp: int) -> str: def get_datetime_from_timestamp(self, timestamp: int) -> str:
"""Convenience fun to get a simple date time from timestamp.""" """Convenience fun to get a simple date time from timestamp."""
return datetime.datetime.fromtimestamp(timestamp).strftime("%Y/%m/%d %I:%M") return datetime.datetime.fromtimestamp(timestamp).strftime("%Y_%m_%d_%I:%M")
def run(self) -> None: def run(self) -> None:
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}"
) )
file_name = f"{EXPORT_DIR}/in_progress.{self.camera}_{self.get_datetime_from_timestamp(self.start_time)}-{self.get_datetime_from_timestamp(self.end_time)}.mp4" file_name = f"{EXPORT_DIR}/in_progress.{self.camera}@{self.get_datetime_from_timestamp(self.start_time)}__{self.get_datetime_from_timestamp(self.end_time)}.mp4"
final_file_name = f"{EXPORT_DIR}/{self.camera}_{self.get_datetime_from_timestamp(self.start_time)}-{self.get_datetime_from_timestamp(self.end_time)}.mp4" final_file_name = f"{EXPORT_DIR}/{self.camera}_{self.get_datetime_from_timestamp(self.start_time)}__{self.get_datetime_from_timestamp(self.end_time)}.mp4"
if (self.end_time - self.start_time) <= MAX_PLAYLIST_SECONDS: if (self.end_time - self.start_time) <= MAX_PLAYLIST_SECONDS:
playlist_lines = f"http://127.0.0.1:5000/vod/start/{self.start_time}/end/{self.end_time}/index.m3u8" playlist_lines = f"http://127.0.0.1:5000/vod/{self.camera}/start/{self.start_time}/end/{self.end_time}/index.m3u8"
ffmpeg_cmd = [ ffmpeg_cmd = [
"ffmpeg", "ffmpeg",
"-hide_banner",
"-y",
"-protocol_whitelist",
"pipe,file,http,tcp",
"-i", "-i",
"/dev/stdin", playlist_lines,
] ]
else: else:
playlist_lines = [] playlist_lines = []
@ -57,15 +62,16 @@ class RecordingExporter(threading.Thread):
while playlist_start < self.end_time: while playlist_start < self.end_time:
playlist_lines.append( playlist_lines.append(
f"file http://127.0.0.1:5000/vod/start/{playlist_start}/end/{min(playlist_start + MAX_PLAYLIST_SECONDS, self.end_time)}/index.m3u8" f"file http://127.0.0.1:5000/vod/{self.camera}/start/{playlist_start}/end/{min(playlist_start + MAX_PLAYLIST_SECONDS, self.end_time)}/index.m3u8"
) )
playlist_start += MAX_PLAYLIST_SECONDS playlist_start += MAX_PLAYLIST_SECONDS
ffmpeg_cmd = [ ffmpeg_cmd = [
"ffmpeg", "ffmpeg",
"-hide_banner",
"-y", "-y",
"-protocol_whitelist", "-protocol_whitelist",
"pipe,file", "pipe,file,http,tcp",
"-f", "-f",
"concat", "concat",
"-safe", "-safe",
@ -92,5 +98,6 @@ class RecordingExporter(threading.Thread):
logger.error(p.stderr) logger.error(p.stderr)
return return
logger.error(f"Updating finalized export {file_name}")
os.rename(file_name, final_file_name) os.rename(file_name, final_file_name)
logger.debug("Finished exporting") logger.error(f"Finished exporting {file_name}")