diff --git a/client/ayon_core/lib/__init__.py b/client/ayon_core/lib/__init__.py index d803af23c7..03ed574081 100644 --- a/client/ayon_core/lib/__init__.py +++ b/client/ayon_core/lib/__init__.py @@ -107,7 +107,6 @@ from .transcoding import ( convert_ffprobe_fps_to_float, get_rescaled_command_arguments, get_media_mime_type, - get_image_info_metadata, ) from .plugin_tools import ( @@ -207,7 +206,6 @@ __all__ = [ "convert_ffprobe_fps_to_float", "get_rescaled_command_arguments", "get_media_mime_type", - "get_image_info_metadata", "compile_list_of_regexes", diff --git a/client/ayon_core/lib/transcoding.py b/client/ayon_core/lib/transcoding.py index 9ff824f395..ead8b621b9 100644 --- a/client/ayon_core/lib/transcoding.py +++ b/client/ayon_core/lib/transcoding.py @@ -835,134 +835,6 @@ def get_ffprobe_streams(path_to_file, logger=None): return get_ffprobe_data(path_to_file, logger)["streams"] -def get_image_info_metadata( - path_to_file, - keys=None, - logger=None, -): - """Get metadata from image file. - - At first it will try to detect if the image input is supported by - OpenImageIO. If it is then it gets the metadata from the image using - OpenImageIO. If it is not supported by OpenImageIO then it will try to - get the metadata using FFprobe. - - Args: - path_to_file (str): Path to image file. - keys (list[str]): List of keys that should be returned. If None then - all keys are returned. Keys are expected all lowercase. - Additional keys are: - - "framerate" - will be created from "r_frame_rate" or - "framespersecond" and evaluated to float value. - logger (logging.Logger): Logger used for logging. - """ - if logger is None: - logger = logging.getLogger(__name__) - - def _ffprobe_metadata_conversion(metadata): - """Convert ffprobe metadata unified format.""" - output = {} - for key, val in metadata.items(): - if key in ("tags", "disposition"): - output.update(val) - else: - output[key] = val - return output - - def _get_video_metadata_from_ffprobe(ffprobe_stream): - """Extract video metadata from ffprobe stream. - - Args: - ffprobe_stream (dict): Stream data obtained from ffprobe. - - Returns: - dict: Video metadata extracted from the ffprobe stream. - """ - video_stream = None - for stream in ffprobe_stream["streams"]: - if stream["codec_type"] == "video": - video_stream = stream - break - metadata_stream = _ffprobe_metadata_conversion(video_stream) - return metadata_stream - - metadata_stream = None - ext = os.path.splitext(path_to_file)[-1].lower() - if ext not in IMAGE_EXTENSIONS: - logger.info(( - "File extension \"{}\" is not supported by OpenImageIO." - " Trying to get metadata using FFprobe." - ).format(ext)) - ffprobe_stream = get_ffprobe_data(path_to_file, logger) - if "streams" in ffprobe_stream and len(ffprobe_stream["streams"]) > 0: - metadata_stream = _get_video_metadata_from_ffprobe(ffprobe_stream) - - if not metadata_stream and is_oiio_supported(): - oiio_stream = get_oiio_info_for_input(path_to_file, logger=logger) - if "attribs" in (oiio_stream or {}): - metadata_stream = {} - for key, val in oiio_stream["attribs"].items(): - if "smpte:" in key.lower(): - key = key.replace("smpte:", "") - metadata_stream[key.lower()] = val - for key, val in oiio_stream.items(): - if key == "attribs": - continue - metadata_stream[key] = val - else: - logger.info(( - "OpenImageIO is not supported on this system." - " Trying to get metadata using FFprobe." - )) - ffprobe_stream = get_ffprobe_data(path_to_file, logger) - if "streams" in ffprobe_stream and len(ffprobe_stream["streams"]) > 0: - metadata_stream = _get_video_metadata_from_ffprobe(ffprobe_stream) - - if not metadata_stream: - logger.warning("Failed to get metadata from image file.") - return {} - - if keys is None: - return metadata_stream - - # create framerate key from available ffmpeg:r_frame_rate - # or oiiotool:framespersecond and evaluate its string expression - # value into flaot value - if ( - "r_frame_rate" in metadata_stream - or "framespersecond" in metadata_stream - ): - rate_info = metadata_stream.get("r_frame_rate") - if rate_info is None: - rate_info = metadata_stream.get("framespersecond") - - # calculate framerate from string expression - if "/" in str(rate_info): - time, frame = str(rate_info).split("/") - rate_info = float(time) / float(frame) - - try: - metadata_stream["framerate"] = float(str(rate_info)) - except Exception as e: - logger.warning( - "Failed to evaluate '{}' value to framerate. Error: {}".format( - rate_info, e) - ) - - # aggregate all required metadata from prepared metadata stream - output = {} - for key in keys: - for k, v in metadata_stream.items(): - if key == k: - output[key] = v - break - if isinstance(v, dict) and key in v: - output[key] = v[key] - break - - return output - - def get_ffmpeg_format_args(ffprobe_data, source_ffmpeg_cmd=None): """Copy format from input metadata for output. diff --git a/client/ayon_core/plugins/load/export_otio.py b/client/ayon_core/plugins/load/export_otio.py index f2351a84f6..bcfae1404a 100644 --- a/client/ayon_core/plugins/load/export_otio.py +++ b/client/ayon_core/plugins/load/export_otio.py @@ -1,3 +1,5 @@ +import logging +import os from pathlib import Path from collections import defaultdict @@ -5,8 +7,15 @@ from qtpy import QtWidgets, QtCore, QtGui from ayon_api import get_representations from ayon_core.pipeline import load, Anatomy -from ayon_core.lib import get_image_info_metadata from ayon_core import resources, style +from ayon_core.lib.transcoding import ( + IMAGE_EXTENSIONS, + get_oiio_info_for_input, +) +from ayon_core.lib import ( + get_ffprobe_data, + is_oiio_supported, +) from ayon_core.pipeline.load import get_representation_path_with_anatomy from ayon_core.tools.utils import show_message_dialog @@ -228,9 +237,10 @@ class ExportOTIOOptionsDialog(QtWidgets.QDialog): version = self._version_by_representation_id[ representation["id"] ] - name = f'{self._version_path_by_id[version["id"]]}/{representation["name"]}'.replace( - "/", "_" - ) + name = ( + f'{self._version_path_by_id[version["id"]]}' + f'/{representation["name"]}' + ).replace("/", "_") clips_data[name] = { "representation": representation, @@ -405,3 +415,138 @@ class ExportOTIOOptionsDialog(QtWidgets.QDialog): 0, timeline_framerate) self.OTIO.adapters.write_to_file(timeline, output_path) + + +def get_image_info_metadata( + path_to_file, + keys=None, + logger=None, +): + """Get flattened metadata from image file + + With combined approach via FFMPEG and OIIOTool. + + At first it will try to detect if the image input is supported by + OpenImageIO. If it is then it gets the metadata from the image using + OpenImageIO. If it is not supported by OpenImageIO then it will try to + get the metadata using FFprobe. + + Args: + path_to_file (str): Path to image file. + keys (list[str]): List of keys that should be returned. If None then + all keys are returned. Keys are expected all lowercase. + Additional keys are: + - "framerate" - will be created from "r_frame_rate" or + "framespersecond" and evaluated to float value. + logger (logging.Logger): Logger used for logging. + """ + if logger is None: + logger = logging.getLogger(__name__) + + def _ffprobe_metadata_conversion(metadata): + """Convert ffprobe metadata unified format.""" + output = {} + for key, val in metadata.items(): + if key in ("tags", "disposition"): + output.update(val) + else: + output[key] = val + return output + + def _get_video_metadata_from_ffprobe(ffprobe_stream): + """Extract video metadata from ffprobe stream. + + Args: + ffprobe_stream (dict): Stream data obtained from ffprobe. + + Returns: + dict: Video metadata extracted from the ffprobe stream. + """ + video_stream = None + for stream in ffprobe_stream["streams"]: + if stream["codec_type"] == "video": + video_stream = stream + break + metadata_stream = _ffprobe_metadata_conversion(video_stream) + return metadata_stream + + metadata_stream = None + ext = os.path.splitext(path_to_file)[-1].lower() + if ext not in IMAGE_EXTENSIONS: + logger.info( + ( + 'File extension "{}" is not supported by OpenImageIO.' + " Trying to get metadata using FFprobe." + ).format(ext) + ) + ffprobe_stream = get_ffprobe_data(path_to_file, logger) + if "streams" in ffprobe_stream and len(ffprobe_stream["streams"]) > 0: + metadata_stream = _get_video_metadata_from_ffprobe(ffprobe_stream) + + if not metadata_stream and is_oiio_supported(): + oiio_stream = get_oiio_info_for_input(path_to_file, logger=logger) + if "attribs" in (oiio_stream or {}): + metadata_stream = {} + for key, val in oiio_stream["attribs"].items(): + if "smpte:" in key.lower(): + key = key.replace("smpte:", "") + metadata_stream[key.lower()] = val + for key, val in oiio_stream.items(): + if key == "attribs": + continue + metadata_stream[key] = val + else: + logger.info( + ( + "OpenImageIO is not supported on this system." + " Trying to get metadata using FFprobe." + ) + ) + ffprobe_stream = get_ffprobe_data(path_to_file, logger) + if "streams" in ffprobe_stream and len(ffprobe_stream["streams"]) > 0: + metadata_stream = _get_video_metadata_from_ffprobe(ffprobe_stream) + + if not metadata_stream: + logger.warning("Failed to get metadata from image file.") + return {} + + if keys is None: + return metadata_stream + + # create framerate key from available ffmpeg:r_frame_rate + # or oiiotool:framespersecond and evaluate its string expression + # value into flaot value + if ( + "r_frame_rate" in metadata_stream + or "framespersecond" in metadata_stream + ): + rate_info = metadata_stream.get("r_frame_rate") + if rate_info is None: + rate_info = metadata_stream.get("framespersecond") + + # calculate framerate from string expression + if "/" in str(rate_info): + time, frame = str(rate_info).split("/") + rate_info = float(time) / float(frame) + + try: + metadata_stream["framerate"] = float(str(rate_info)) + except Exception as e: + logger.warning( + "Failed to evaluate '{}' value to framerate. Error: {}".format( + rate_info, e + ) + ) + + # aggregate all required metadata from prepared metadata stream + output = {} + for key in keys: + for k, v in metadata_stream.items(): + if key == k: + output[key] = v + break + if isinstance(v, dict) and key in v: + output[key] = v[key] + break + + return output