From 1c06bf2ecf15579c78a2827f11bf80e2907d8a98 Mon Sep 17 00:00:00 2001 From: Evan Blaudy Date: Tue, 19 Sep 2023 18:31:30 +0200 Subject: [PATCH] [cli] add a command to generate tiles and update file previews metadatas in the same time --- zou/app/services/preview_files_service.py | 124 ++++++++++++++++++---- zou/app/utils/commands.py | 13 ++- zou/cli.py | 17 ++- zou/utils/movie.py | 27 +---- 4 files changed, 129 insertions(+), 52 deletions(-) diff --git a/zou/app/services/preview_files_service.py b/zou/app/services/preview_files_service.py index 7c71948b21..0b37c00517 100644 --- a/zou/app/services/preview_files_service.py +++ b/zou/app/services/preview_files_service.py @@ -650,20 +650,18 @@ def generate_tiles_for_movie_previews(): path = extract_tile_from_preview_file(preview_file.serialize()) file_store.add_picture("tiles", str(preview_file.id), path) print( - f"Tile generated preview file for {preview_file.id}", + f"Tile generated for preview file {preview_file.id}", ) except Exception as e: print( - "Failed to generate tile for preview file %s: %s", - str(preview_file.id), - e, + f"Failed to generate tile for preview file {preview_file.id}: {e}" ) return preview_files -def reset_movie_file_metadata(): +def reset_movie_files_metadata(): """ - Reset preview file size information of open projects. + Reset preview files size informations of open projects. """ preview_files = ( PreviewFile.query.join(Task) @@ -683,9 +681,8 @@ def reset_movie_file_metadata(): str(preview_file.id), "mp4", ) - size = movie.get_movie_size(preview_file_path) file_size = os.path.getsize(preview_file_path) - width, height = size + width, height = movie.get_movie_size(preview_file_path) update_preview_file_raw( preview_file, { @@ -695,19 +692,17 @@ def reset_movie_file_metadata(): }, ) print( - f"Size information stored for {preview_file.id}", + f"Size information stored preview file {preview_file.id}", ) except Exception as e: print( - "Failed to store information for preview file %s: %s", - str(preview_file.id), - e, + f"Failed to store information for preview file {preview_file.id}: {e}" ) -def reset_picture_file_metadata(): +def reset_picture_files_metadata(): """ - Reset preview file size information of open projects. + Reset preview files size informations of open projects. """ preview_files = ( PreviewFile.query.join(Task) @@ -738,11 +733,104 @@ def reset_picture_file_metadata(): }, ) print( - f"Size information stored for {preview_file.id}", + f"Size information stored for preview file {preview_file.id}", ) except Exception as e: print( - "Failed to store information for preview file %s: %s", - str(preview_file.id), - e, + f"Failed to store information for preview file {preview_file.id}: {e}" + ) + + +def generate_tiles_and_reset_preview_files_metadata(): + """ + Generate tiles for all movie previews and reset previews file size + informations of open projects. + """ + preview_files = ( + PreviewFile.query.join(Task) + .join(Project) + .join(ProjectStatus) + .filter(ProjectStatus.name.in_(("Active", "open", "Open"))) + .filter(PreviewFile.status.not_in(("broken", "processing"))) + .filter(PreviewFile.extension.in_(("mp4", "png"))) + ) + preview_file_already_in_cache = False + for preview_file in preview_files: + preview_file_id = str(preview_file.id) + prefix = "previews" if preview_file.extension == "mp4" else "original" + if config.FS_BACKEND != "local": + preview_file_already_in_cache = os.path.isfile( + os.path.join( + config.TMP_DIR, + "cache-%s-%s.%s" + % (prefix, preview_file_id, preview_file.extension), + ) ) + try: + try: + preview_file_path = fs.get_file_path_and_file( + config, + file_store.get_local_movie_path + if preview_file.extension == "mp4" + else file_store.get_local_picture_path, + file_store.open_movie + if preview_file.extension == "mp4" + else file_store.open_picture, + prefix, + preview_file_id, + preview_file.extension, + ) + except Exception as e: + print(f"Failed to get preview file {preview_file_id}: {e}") + continue + try: + if preview_file.extension == "mp4": + project = get_project_from_preview_file(preview_file_id) + fps = get_preview_file_fps(project) + extracted_tile_path = movie.generate_tile( + preview_file_path, fps + ) + file_store.add_picture( + "tiles", preview_file_id, extracted_tile_path + ) + print( + f"Tile generated for preview file {preview_file_id}", + ) + except Exception as e: + print( + f"Failed to generate tile for preview file {preview_file_id}: {e}" + ) + try: + if preview_file.extension == "mp4": + width, height = movie.get_movie_size(preview_file_path) + else: + width, height = thumbnail_utils.get_dimensions( + preview_file_path + ) + file_size = os.path.getsize(preview_file_path) + update_preview_file_raw( + preview_file, + { + "width": width, + "height": height, + "file_size": file_size, + }, + ) + print( + f"Size information stored for preview file {preview_file_id}", + ) + except Exception as e: + print( + f"Failed to store information for preview file {preview_file_id}: {e}", + ) + finally: + if ( + config.FS_BACKEND != "local" + and not preview_file_already_in_cache + ): + try: + os.remove(preview_file_path) + except: + pass + + return preview_files diff --git a/zou/app/utils/commands.py b/zou/app/utils/commands.py index 3da5c43cf6..e808b7ae9a 100644 --- a/zou/app/utils/commands.py +++ b/zou/app/utils/commands.py @@ -631,11 +631,16 @@ def generate_tiles(): preview_files_service.generate_tiles_for_movie_previews() -def reset_movie_file_metadata(): +def reset_movie_files_metadata(): with app.app_context(): - preview_files_service.reset_movie_file_metadata() + preview_files_service.reset_movie_files_metadata() -def reset_picture_file_metadata(): +def reset_picture_files_metadata(): with app.app_context(): - preview_files_service.reset_picture_file_metadata() + preview_files_service.reset_picture_files_metadata() + + +def generate_tiles_and_reset_preview_files_metadata(): + with app.app_context(): + preview_files_service.generate_tiles_and_reset_preview_files_metadata() diff --git a/zou/cli.py b/zou/cli.py index eaf55ca06f..0f07d19216 100755 --- a/zou/cli.py +++ b/zou/cli.py @@ -469,19 +469,28 @@ def generate_tiles(): @cli.command() -def reset_movie_file_metadata(): +def reset_movie_files_metadata(): """ Store height and width metadata for all movie previews in the database. """ - commands.reset_movie_file_metadata() + commands.reset_movie_files_metadata() @cli.command() -def reset_picture_file_metadata(): +def reset_picture_files_metadata(): """ Store height and width metadata for all picture previews in the database. """ - commands.reset_picture_file_metadata() + commands.reset_picture_files_metadata() + + +@cli.command() +def generate_tiles_and_reset_preview_files_metadata(): + """ + Generate tiles and store height and width metadata for all pictures/movies + previews in the database. + """ + commands.generate_tiles_and_reset_preview_files_metadata() if __name__ == "__main__": diff --git a/zou/utils/movie.py b/zou/utils/movie.py index 57767a9fd0..39b0ba8e7a 100644 --- a/zou/utils/movie.py +++ b/zou/utils/movie.py @@ -94,30 +94,6 @@ def extract_frame_from_movie(movie_path, frame_number, movie_fps): return file_target_path -def get_all_frames(movie_path): - """ - Generate thumbnails to represent the movie given at movie path. It - takes a picture at each frame of the movie. - """ - folder_path = os.path.join(os.getcwd(), "movie_frames") - os.makedirs(folder_path, exist_ok=True) - file_source_name = os.path.basename(movie_path) - file_target_name = f"{file_source_name[:-4]}_%d.png" - file_target_path = os.path.join(folder_path, file_target_name) - - try: - ( - ffmpeg.input(movie_path) - .output(file_target_path, vsync=0) - .run(quiet=True) - ) - except ffmpeg._run.Error as e: - print(f"Error generating thumbnails: {e}") - raise e - - return folder_path - - def generate_tile(movie_path, movie_fps): """ Generates a tile from a movie. @@ -126,7 +102,6 @@ def generate_tile(movie_path, movie_fps): file_source_name = os.path.basename(movie_path) file_target_name = f"{file_source_name[:-4]}_tile.png" file_target_path = os.path.join(folder_path, file_target_name) - probe = ffmpeg.probe(movie_path) duration_in_seconds = float(probe["streams"][0]["duration"]) float_movie_fps = float(movie_fps) @@ -139,7 +114,7 @@ def generate_tile(movie_path, movie_fps): try: ffmpeg.input(movie_path).output( file_target_path, vf=f"scale={width}:{height},tile=8x{rows}" - ).run(quiet=True) + ).overwrite_output().run(quiet=True) except ffmpeg._run.Error as e: log_ffmpeg_error(e, "An error occured while generating the tile.") raise e