Save exports that are in progress

This commit is contained in:
Nicolas Mowen 2024-04-19 08:16:12 -06:00
parent bfdcd3bb48
commit b5d00efa5b
3 changed files with 32 additions and 27 deletions

View File

@ -80,9 +80,11 @@ class Recordings(Model): # type: ignore[misc]
class Export(Model): # type: ignore[misc] class Export(Model): # type: ignore[misc]
id = CharField(null=False, primary_key=True, max_length=30) id = CharField(null=False, primary_key=True, max_length=30)
camera = CharField(index=True, max_length=20) camera = CharField(index=True, max_length=20)
name = CharField(index=True, max_length=100)
date = DateTimeField() date = DateTimeField()
video_path = CharField(unique=True) video_path = CharField(unique=True)
thumb_path = CharField(unique=True) thumb_path = CharField(unique=True)
in_progress = BooleanField()
class ReviewSegment(Model): # type: ignore[misc] class ReviewSegment(Model): # type: ignore[misc]

View File

@ -65,12 +65,11 @@ 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_%H_%M") return datetime.datetime.fromtimestamp(timestamp).strftime("%Y/%m/%d %H:%M")
def save_thumbnail(self, id: str) -> str: def save_thumbnail(self, id: str) -> str:
thumb_path = os.path.join(CLIPS_DIR, f"export/{id}.webp") thumb_path = os.path.join(CLIPS_DIR, f"export/{id}.webp")
if datetime.datetime.fromtimestamp( if datetime.datetime.fromtimestamp(
self.start_time self.start_time
) < datetime.datetime.now().replace(minute=0, second=0): ) < datetime.datetime.now().replace(minute=0, second=0):
@ -157,12 +156,26 @@ 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}"
) )
file_name = ( export_id = f"{self.camera}_{''.join(random.choices(string.ascii_lowercase + string.digits, k=6))}"
export_name = (
self.user_provided_name self.user_provided_name
or f"{self.camera}_{self.get_datetime_from_timestamp(self.start_time)}__{self.get_datetime_from_timestamp(self.end_time)}" or f"{self.camera} {self.get_datetime_from_timestamp(self.start_time)} {self.get_datetime_from_timestamp(self.end_time)}"
) )
file_path = f"{EXPORT_DIR}/in_progress.{file_name}.mp4" video_path = f"{EXPORT_DIR}/{export_id}.mp4"
final_file_path = f"{EXPORT_DIR}/{file_name}.mp4"
thumb_path = self.save_thumbnail(export_id)
Export.insert(
{
Export.id: export_id,
Export.camera: self.camera,
Export.name: export_name,
Export.date: self.start_time,
Export.video_path: video_path,
Export.thumb_path: thumb_path,
Export.in_progress: True,
}
).execute()
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/{self.camera}/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"
@ -201,14 +214,14 @@ class RecordingExporter(threading.Thread):
if self.playback_factor == PlaybackFactorEnum.realtime: if self.playback_factor == PlaybackFactorEnum.realtime:
ffmpeg_cmd = ( ffmpeg_cmd = (
f"ffmpeg -hide_banner {ffmpeg_input} -c copy -movflags +faststart {file_path}" f"ffmpeg -hide_banner {ffmpeg_input} -c copy -movflags +faststart {video_path}"
).split(" ") ).split(" ")
elif self.playback_factor == PlaybackFactorEnum.timelapse_25x: elif self.playback_factor == PlaybackFactorEnum.timelapse_25x:
ffmpeg_cmd = ( ffmpeg_cmd = (
parse_preset_hardware_acceleration_encode( parse_preset_hardware_acceleration_encode(
self.config.ffmpeg.hwaccel_args, self.config.ffmpeg.hwaccel_args,
f"{TIMELAPSE_DATA_INPUT_ARGS} {ffmpeg_input}", f"{TIMELAPSE_DATA_INPUT_ARGS} {ffmpeg_input}",
f"{self.config.cameras[self.camera].record.export.timelapse_args} -movflags +faststart {file_path}", f"{self.config.cameras[self.camera].record.export.timelapse_args} -movflags +faststart {video_path}",
EncodeTypeEnum.timelapse, EncodeTypeEnum.timelapse,
) )
).split(" ") ).split(" ")
@ -226,23 +239,13 @@ class RecordingExporter(threading.Thread):
f"Failed to export recording for command {' '.join(ffmpeg_cmd)}" f"Failed to export recording for command {' '.join(ffmpeg_cmd)}"
) )
logger.error(p.stderr) logger.error(p.stderr)
Path(file_path).unlink(missing_ok=True) Path(video_path).unlink(missing_ok=True)
Export.delete().where(Export.id == export_id).execute()
Path(thumb_path).unlink(missing_ok=True)
return return
else:
Export.update({Export.in_progress: False}).where(
Export.id == export_id
).execute()
logger.debug(f"Updating finalized export {file_path}") logger.debug(f"Finished exporting {video_path}")
os.rename(file_path, final_file_path)
export_id = f"{self.camera}_{''.join(random.choices(string.ascii_lowercase + string.digits, k=6))}"
thumb_path = self.save_thumbnail(export_id)
Export.insert(
{
Export.id: export_id,
Export.camera: self.camera,
Export.date: self.start_time,
Export.video_path: final_file_path,
Export.thumb_path: thumb_path,
}
)
logger.debug(f"Finished exporting {file_path}")

View File

@ -28,7 +28,7 @@ SQL = pw.SQL
def migrate(migrator, database, fake=False, **kwargs): def migrate(migrator, database, fake=False, **kwargs):
migrator.sql( migrator.sql(
'CREATE TABLE IF NOT EXISTS "export" ("id" VARCHAR(30) NOT NULL PRIMARY KEY, "camera" VARCHAR(20) NOT NULL, "date" DATETIME NOT NULL, "video_path" VARCHAR(255) NOT NULL, "thumb_path" VARCHAR(255) NOT NULL)' 'CREATE TABLE IF NOT EXISTS "export" ("id" VARCHAR(30) NOT NULL PRIMARY KEY, "camera" VARCHAR(20) NOT NULL, "name" VARCHAR(100) NOT NULL, "date" DATETIME NOT NULL, "video_path" VARCHAR(255) NOT NULL, "thumb_path" VARCHAR(255) NOT NULL, "in_progress" INTEGER NOT NULL)'
) )
migrator.sql( migrator.sql(
'CREATE INDEX IF NOT EXISTS "export_camera" ON "export" ("camera")' 'CREATE INDEX IF NOT EXISTS "export_camera" ON "export" ("camera")'