From 14b704fcd774547bc249c3b88ab1dd30ee2fda83 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 31 Jan 2022 12:03:58 +0100 Subject: [PATCH] flame: moving openclip solver to plugins modul - starting with loader class functionality --- openpype/hosts/flame/api/__init__.py | 7 +- openpype/hosts/flame/api/plugin.py | 291 +++++++++++++++++++++++ openpype/hosts/flame/api/render_utils.py | 252 -------------------- 3 files changed, 297 insertions(+), 253 deletions(-) diff --git a/openpype/hosts/flame/api/__init__.py b/openpype/hosts/flame/api/__init__.py index 8e5418c78b..2aeb0d9c16 100644 --- a/openpype/hosts/flame/api/__init__.py +++ b/openpype/hosts/flame/api/__init__.py @@ -52,7 +52,10 @@ from .menu import ( ) from .plugin import ( Creator, - PublishableClip + PublishableClip, + ClipLoader, + OpenClipSolver + ) from .workio import ( open_file, @@ -122,6 +125,8 @@ __all__ = [ # plugin "Creator", "PublishableClip", + "ClipLoader", + "OpenClipSolver", # workio "open_file", diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index f34999bcf3..a83f9a3b28 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -1,7 +1,15 @@ +import os import re +import shutil +import sys +from avalon.vendor import qargparse +from xml.etree import ElementTree as ET +import shutil +import six from Qt import QtWidgets, QtCore import openpype.api as openpype from openpype import style +import avalon.api as avalon from . import ( lib as flib, pipeline as fpipeline, @@ -644,3 +652,286 @@ class PublishableClip: # Publishing plugin functions # Loader plugin functions + +class ClipLoader(avalon.Loader): + """A basic clip loader for Flame + + This will implement the basic behavior for a loader to inherit from that + will containerize the reference and will implement the `remove` and + `update` logic. + + """ + + options = [ + qargparse.Boolean( + "handles", + label="Set handles", + default=0, + help="Also set handles to clip as In/Out marks" + ) + ] + + def load( + self, + context, + name=None, + namespace=None, + options=None + ): + pass + + def update(self, container, representation): + """Update an existing `container` + """ + pass + + def remove(self, container): + """Remove an existing `container` + """ + pass + + +class OpenClipSolver: + media_script_path = "/opt/Autodesk/mio/current/dl_get_media_info" + tmp_name = "_tmp.clip" + tmp_file = None + create_new_clip = False + + out_feed_nb_ticks = None + out_feed_fps = None + out_feed_drop_mode = None + + def __init__(self, name, openclip_file_path, feed_data): + # test if media script paht exists + self._validate_media_script_path() + + # new feed variables: + feed_path = feed_data["path"] + self.feed_version_name = feed_data["version"] + self.feed_colorspace = feed_data.get("colorspace") + + # derivate other feed variables + self.feed_basename = os.path.basename(feed_path) + self.feed_dir = os.path.dirname(feed_path) + self.feed_ext = os.path.splitext(self.feed_basename)[1][1:].lower() + + if not os.path.isfile(openclip_file_path): + # openclip does not exist yet and will be created + self.tmp_file = self.out_file = openclip_file_path + self.create_new_clip = True + + else: + # output a temp file + self.out_file = openclip_file_path + self.tmp_file = os.path.join(self.feed_dir, self.tmp_name) + self._clear_tmp_file() + + print("Temp File: {}".format(self.tmp_file)) + + def _validate_media_script_path(self): + if not os.path.isfile(self.media_script_path): + raise IOError("Media Scirpt does not exist: `{}`".format( + self.media_script_path)) + + def _get_media_info_args(self): + # Create cmd arguments for gettig xml file info file + cmd_args = [ + self.media_script_path, + "-e", self.feed_ext, + "-o", self.tmp_file, + self.feed_dir + ] + + # execute creation of clip xml template data + try: + flib.run_subprocess(cmd_args) + except TypeError: + print("Error createing self.tmp_file") + six.reraise(*sys.exc_info()) + + def _clear_tmp_file(self): + if os.path.isfile(self.tmp_file): + os.remove(self.tmp_file) + + def _clear_handler(self, xml_object): + for handler in xml_object.findall("./handler"): + print("Handler found") + xml_object.remove(handler) + + def _create_new_open_clip(self): + print("Building new openClip") + + tmp_xml = ET.parse(self.tmp_file) + + tmp_xml_feeds = tmp_xml.find('tracks/track/feeds') + tmp_xml_feeds.set('currentVersion', self.feed_version_name) + for tmp_feed in tmp_xml_feeds: + tmp_feed.set('vuid', self.feed_version_name) + + # add colorspace if any is set + if self.feed_colorspace: + self._add_colorspace(tmp_feed, self.feed_colorspace) + + self._clear_handler(tmp_feed) + + tmp_xml_versions_obj = tmp_xml.find('versions') + tmp_xml_versions_obj.set('currentVersion', self.feed_version_name) + for xml_new_version in tmp_xml_versions_obj: + xml_new_version.set('uid', self.feed_version_name) + xml_new_version.set('type', 'version') + + xml_data = self._fix_xml_data(tmp_xml) + print("Adding feed version: {}".format(self.feed_basename)) + + self._write_result_xml_to_file(xml_data) + + print("openClip Updated: %s" % self.tmp_file) + + def _update_open_clip(self): + print("Updating openClip ..") + + out_xml = ET.parse(self.out_file) + tmp_xml = ET.parse(self.tmp_file) + + print(">> out_xml: {}".format(out_xml)) + print(">> tmp_xml: {}".format(tmp_xml)) + + # Get new feed from tmp file + tmp_xml_feed = tmp_xml.find('tracks/track/feeds/feed') + + self._clear_handler(tmp_xml_feed) + self._get_time_info_from_origin(out_xml) + + if self.out_feed_fps: + tmp_feed_fps_obj = tmp_xml_feed.find( + "startTimecode/rate") + tmp_feed_fps_obj.text = self.out_feed_fps + if self.out_feed_nb_ticks: + tmp_feed_nb_ticks_obj = tmp_xml_feed.find( + "startTimecode/nbTicks") + tmp_feed_nb_ticks_obj.text = self.out_feed_nb_ticks + if self.out_feed_drop_mode: + tmp_feed_drop_mode_obj = tmp_xml_feed.find( + "startTimecode/dropMode") + tmp_feed_drop_mode_obj.text = self.out_feed_drop_mode + + new_path_obj = tmp_xml_feed.find( + "spans/span/path") + new_path = new_path_obj.text + + feed_added = False + if not self._feed_exists(out_xml, new_path): + tmp_xml_feed.set('vuid', self.feed_version_name) + # Append new temp file feed to .clip source out xml + out_track = out_xml.find("tracks/track") + # add colorspace if any is set + if self.feed_colorspace: + self._add_colorspace(tmp_xml_feed, self.feed_colorspace) + + out_feeds = out_track.find('feeds') + out_feeds.set('currentVersion', self.feed_version_name) + out_feeds.append(tmp_xml_feed) + + print( + "Appending new feed: {}".format( + self.feed_version_name)) + feed_added = True + + if feed_added: + # Append vUID to versions + out_xml_versions_obj = out_xml.find('versions') + out_xml_versions_obj.set( + 'currentVersion', self.feed_version_name) + new_version_obj = ET.Element( + "version", {"type": "version", "uid": self.feed_version_name}) + out_xml_versions_obj.insert(0, new_version_obj) + + xml_data = self._fix_xml_data(out_xml) + + # fist create backup + self._create_openclip_backup_file(self.out_file) + + print("Adding feed version: {}".format(self.feed_version_name)) + + self._write_result_xml_to_file(xml_data) + + print("openClip Updated: {}".format(self.out_file)) + + self._clear_tmp_file() + + def _get_time_info_from_origin(self, xml_data): + try: + for out_track in xml_data.iter('track'): + for out_feed in out_track.iter('feed'): + out_feed_nb_ticks_obj = out_feed.find( + 'startTimecode/nbTicks') + self.out_feed_nb_ticks = out_feed_nb_ticks_obj.text + out_feed_fps_obj = out_feed.find( + 'startTimecode/rate') + self.out_feed_fps = out_feed_fps_obj.text + out_feed_drop_mode_obj = out_feed.find( + 'startTimecode/dropMode') + self.out_feed_drop_mode = out_feed_drop_mode_obj.text + break + else: + continue + except Exception as msg: + print(msg) + + def _feed_exists(self, xml_data, path): + # loop all available feed paths and check if + # the path is not already in file + for src_path in xml_data.iter('path'): + if path == src_path.text: + print("Not appending file as it already is in .clip file") + return True + + def _fix_xml_data(self, xml_data): + xml_root = xml_data.getroot() + self._clear_handler(xml_root) + return ET.tostring(xml_root).decode('utf-8') + + def maintain_clip(self): + self._get_media_info_args() + + if self.create_new_clip: + # New openClip + self._create_new_open_clip() + else: + self._update_open_clip() + + def _write_result_xml_to_file(self, xml_data): + with open(self.out_file, "w") as f: + f.write(xml_data) + + def _create_openclip_backup_file(self, file): + bck_file = "{}.bak".format(file) + # if backup does not exist + if not os.path.isfile(bck_file): + shutil.copy2(file, bck_file) + else: + # in case it exists and is already multiplied + created = False + for _i in range(1, 99): + bck_file = "{name}.bak.{idx:0>2}".format( + name=file, + idx=_i) + # create numbered backup file + if not os.path.isfile(bck_file): + shutil.copy2(file, bck_file) + created = True + break + # in case numbered does not exists + if not created: + bck_file = "{}.bak.last".format(file) + shutil.copy2(file, bck_file) + + def _add_colorspace(self, feed_obj, profile_name): + feed_storage_obj = feed_obj.find("storageFormat") + feed_clr_obj = feed_storage_obj.find("colourSpace") + if not feed_clr_obj: + feed_clr_obj = ET.Element( + "colourSpace", {"type": "string"}) + feed_storage_obj.append(feed_clr_obj) + + feed_clr_obj.text = profile_name diff --git a/openpype/hosts/flame/api/render_utils.py b/openpype/hosts/flame/api/render_utils.py index fe406f1c94..1b086646cc 100644 --- a/openpype/hosts/flame/api/render_utils.py +++ b/openpype/hosts/flame/api/render_utils.py @@ -1,10 +1,4 @@ import os -import sys -import traceback -from xml.etree import ElementTree as ET -import shutil -import openpype.lib -import six def export_clip(export_path, clip, preset_path, **kwargs): @@ -129,249 +123,3 @@ def get_preset_path_by_xml_name(xml_preset_name): # if nothing found then return False return False - - -class OpenClip: - media_script_path = "/opt/Autodesk/mio/current/dl_get_media_info" - tmp_name = "_tmp.clip" - tmp_file = None - create_new_clip = False - - out_feed_nb_ticks = None - out_feed_fps = None - out_feed_drop_mode = None - - def __init__(self, name, openclip_file_path, feed_data): - # test if media script paht exists - self._validate_media_script_path() - - # new feed variables: - feed_path = feed_data["path"] - self.feed_version_name = feed_data["version"] - self.feed_colorspace = feed_data.get("colorspace") - - # derivate other feed variables - self.feed_basename = os.path.basename(feed_path) - self.feed_dir = os.path.dirname(feed_path) - self.feed_ext = os.path.splitext(self.feed_basename)[1][1:].lower() - - if not os.path.isfile(openclip_file_path): - # openclip does not exist yet and will be created - self.tmp_file = self.out_file = openclip_file_path - self.create_new_clip = True - - else: - # output a temp file - self.out_file = openclip_file_path - self.tmp_file = os.path.join(self.feed_dir, self.tmp_name) - self._clear_tmp_file() - - print("Temp File: {}".format(self.tmp_file)) - - def _validate_media_script_path(self): - if not os.path.isfile(self.media_script_path): - raise IOError("Media Scirpt does not exist: `{}`".format( - self.media_script_path)) - - def _get_media_info_args(self): - # Create cmd arguments for gettig xml file info file - cmd_args = [ - self.media_script_path, - "-e", self.feed_ext, - "-o", self.tmp_file, - self.feed_dir - ] - - # execute creation of clip xml template data - try: - openpype.lib.run_subprocess(cmd_args) - except TypeError: - print("Error createing self.tmp_file") - six.reraise(*sys.exc_info()) - - def _clear_tmp_file(self): - if os.path.isfile(self.tmp_file): - os.remove(self.tmp_file) - - def _clear_handler(self, xml_object): - for handler in xml_object.findall("./handler"): - print("Handler found") - xml_object.remove(handler) - - def _create_new_open_clip(self): - print("Building new openClip") - - tmp_xml = ET.parse(self.tmp_file) - - tmp_xml_feeds = tmp_xml.find('tracks/track/feeds') - tmp_xml_feeds.set('currentVersion', self.feed_version_name) - for tmp_feed in tmp_xml_feeds: - tmp_feed.set('vuid', self.feed_version_name) - - # add colorspace if any is set - if self.feed_colorspace: - self._add_colorspace(tmp_feed, self.feed_colorspace) - - self._clear_handler(tmp_feed) - - tmp_xml_versions_obj = tmp_xml.find('versions') - tmp_xml_versions_obj.set('currentVersion', self.feed_version_name) - for xml_new_version in tmp_xml_versions_obj: - xml_new_version.set('uid', self.feed_version_name) - xml_new_version.set('type', 'version') - - xml_data = self._fix_xml_data(tmp_xml) - print("Adding feed version: {}".format(self.feed_basename)) - - self._write_result_xml_to_file(xml_data) - - print("openClip Updated: %s" % self.tmp_file) - - def _update_open_clip(self): - print("Updating openClip ..") - - out_xml = ET.parse(self.out_file) - tmp_xml = ET.parse(self.tmp_file) - - print(">> out_xml: {}".format(out_xml)) - print(">> tmp_xml: {}".format(tmp_xml)) - - # Get new feed from tmp file - tmp_xml_feed = tmp_xml.find('tracks/track/feeds/feed') - - self._clear_handler(tmp_xml_feed) - self._get_time_info_from_origin(out_xml) - - if self.out_feed_fps: - tmp_feed_fps_obj = tmp_xml_feed.find( - "startTimecode/rate") - tmp_feed_fps_obj.text = self.out_feed_fps - if self.out_feed_nb_ticks: - tmp_feed_nb_ticks_obj = tmp_xml_feed.find( - "startTimecode/nbTicks") - tmp_feed_nb_ticks_obj.text = self.out_feed_nb_ticks - if self.out_feed_drop_mode: - tmp_feed_drop_mode_obj = tmp_xml_feed.find( - "startTimecode/dropMode") - tmp_feed_drop_mode_obj.text = self.out_feed_drop_mode - - new_path_obj = tmp_xml_feed.find( - "spans/span/path") - new_path = new_path_obj.text - - feed_added = False - if not self._feed_exists(out_xml, new_path): - tmp_xml_feed.set('vuid', self.feed_version_name) - # Append new temp file feed to .clip source out xml - out_track = out_xml.find("tracks/track") - # add colorspace if any is set - if self.feed_colorspace: - self._add_colorspace(tmp_xml_feed, self.feed_colorspace) - - out_feeds = out_track.find('feeds') - out_feeds.set('currentVersion', self.feed_version_name) - out_feeds.append(tmp_xml_feed) - - print( - "Appending new feed: {}".format( - self.feed_version_name)) - feed_added = True - - if feed_added: - # Append vUID to versions - out_xml_versions_obj = out_xml.find('versions') - out_xml_versions_obj.set( - 'currentVersion', self.feed_version_name) - new_version_obj = ET.Element( - "version", {"type": "version", "uid": self.feed_version_name}) - out_xml_versions_obj.insert(0, new_version_obj) - - xml_data = self._fix_xml_data(out_xml) - - # fist create backup - self._create_openclip_backup_file(self.out_file) - - print("Adding feed version: {}".format(self.feed_version_name)) - - self._write_result_xml_to_file(xml_data) - - print("openClip Updated: {}".format(self.out_file)) - - self._clear_tmp_file() - - def _get_time_info_from_origin(self, xml_data): - try: - for out_track in xml_data.iter('track'): - for out_feed in out_track.iter('feed'): - out_feed_nb_ticks_obj = out_feed.find( - 'startTimecode/nbTicks') - self.out_feed_nb_ticks = out_feed_nb_ticks_obj.text - out_feed_fps_obj = out_feed.find( - 'startTimecode/rate') - self.out_feed_fps = out_feed_fps_obj.text - out_feed_drop_mode_obj = out_feed.find( - 'startTimecode/dropMode') - self.out_feed_drop_mode = out_feed_drop_mode_obj.text - break - else: - continue - except Exception as msg: - print(msg) - - def _feed_exists(self, xml_data, path): - # loop all available feed paths and check if - # the path is not already in file - for src_path in xml_data.iter('path'): - if path == src_path.text: - print("Not appending file as it already is in .clip file") - return True - - def _fix_xml_data(self, xml_data): - xml_root = xml_data.getroot() - self._clear_handler(xml_root) - return ET.tostring(xml_root).decode('utf-8') - - def maintain_clip(self): - self._get_media_info_args() - - if self.create_new_clip: - # New openClip - self._create_new_open_clip() - else: - self._update_open_clip() - - def _write_result_xml_to_file(self, xml_data): - with open(self.out_file, "w") as f: - f.write(xml_data) - - def _create_openclip_backup_file(self, file): - bck_file = "{}.bak".format(file) - # if backup does not exist - if not os.path.isfile(bck_file): - shutil.copy2(file, bck_file) - else: - # in case it exists and is already multiplied - created = False - for _i in range(1, 99): - bck_file = "{name}.bak.{idx:0>2}".format( - name=file, - idx=_i) - # create numbered backup file - if not os.path.isfile(bck_file): - shutil.copy2(file, bck_file) - created = True - break - # in case numbered does not exists - if not created: - bck_file = "{}.bak.last".format(file) - shutil.copy2(file, bck_file) - - def _add_colorspace(self, feed_obj, profile_name): - feed_storage_obj = feed_obj.find("storageFormat") - feed_clr_obj = feed_storage_obj.find("colourSpace") - if not feed_clr_obj: - feed_clr_obj = ET.Element( - "colourSpace", {"type": "string"}) - feed_storage_obj.append(feed_clr_obj) - - feed_clr_obj.text = profile_name