From 07771a40521a63111b2e69ebe12e9686a0a443d7 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 21 Oct 2021 15:53:23 +0200 Subject: [PATCH 001/492] Flame: starting with otio export modul --- openpype/hosts/flame/otio/__init__.py | 0 openpype/hosts/flame/otio/flame_export.py | 448 ++++++++++++++++++++++ openpype/hosts/flame/otio/utils.py | 80 ++++ 3 files changed, 528 insertions(+) create mode 100644 openpype/hosts/flame/otio/__init__.py create mode 100644 openpype/hosts/flame/otio/flame_export.py create mode 100644 openpype/hosts/flame/otio/utils.py diff --git a/openpype/hosts/flame/otio/__init__.py b/openpype/hosts/flame/otio/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openpype/hosts/flame/otio/flame_export.py b/openpype/hosts/flame/otio/flame_export.py new file mode 100644 index 00000000000..af4322e3d92 --- /dev/null +++ b/openpype/hosts/flame/otio/flame_export.py @@ -0,0 +1,448 @@ +""" compatibility OpenTimelineIO 0.12.0 and newer +""" + +import os +import re +import sys +import ast +from compiler.ast import flatten +import opentimelineio as otio +from . import utils +import hiero.core +import hiero.ui + +self = sys.modules[__name__] +self.track_types = { + hiero.core.VideoTrack: otio.schema.TrackKind.Video, + hiero.core.AudioTrack: otio.schema.TrackKind.Audio +} +self.project_fps = None +self.marker_color_map = { + "magenta": otio.schema.MarkerColor.MAGENTA, + "red": otio.schema.MarkerColor.RED, + "yellow": otio.schema.MarkerColor.YELLOW, + "green": otio.schema.MarkerColor.GREEN, + "cyan": otio.schema.MarkerColor.CYAN, + "blue": otio.schema.MarkerColor.BLUE, +} +self.timeline = None +self.include_tags = True + + +def get_current_hiero_project(remove_untitled=False): + projects = flatten(hiero.core.projects()) + if not remove_untitled: + return next(iter(projects)) + + # if remove_untitled + for proj in projects: + if "Untitled" in proj.name(): + proj.close() + else: + return proj + + +def create_otio_rational_time(frame, fps): + return otio.opentime.RationalTime( + float(frame), + float(fps) + ) + + +def create_otio_time_range(start_frame, frame_duration, fps): + return otio.opentime.TimeRange( + start_time=create_otio_rational_time(start_frame, fps), + duration=create_otio_rational_time(frame_duration, fps) + ) + + +def _get_metadata(item): + if hasattr(item, 'metadata'): + return {key: value for key, value in dict(item.metadata()).items()} + return {} + + +def create_time_effects(otio_clip, track_item): + # get all subtrack items + subTrackItems = flatten(track_item.parent().subTrackItems()) + speed = track_item.playbackSpeed() + + otio_effect = None + # retime on track item + if speed != 1.: + # make effect + otio_effect = otio.schema.LinearTimeWarp() + otio_effect.name = "Speed" + otio_effect.time_scalar = speed + otio_effect.metadata = {} + + # freeze frame effect + if speed == 0.: + otio_effect = otio.schema.FreezeFrame() + otio_effect.name = "FreezeFrame" + otio_effect.metadata = {} + + if otio_effect: + # add otio effect to clip effects + otio_clip.effects.append(otio_effect) + + # loop trought and get all Timewarps + for effect in subTrackItems: + if ((track_item not in effect.linkedItems()) + and (len(effect.linkedItems()) > 0)): + continue + # avoid all effect which are not TimeWarp and disabled + if "TimeWarp" not in effect.name(): + continue + + if not effect.isEnabled(): + continue + + node = effect.node() + name = node["name"].value() + + # solve effect class as effect name + _name = effect.name() + if "_" in _name: + effect_name = re.sub(r"(?:_)[_0-9]+", "", _name) # more numbers + else: + effect_name = re.sub(r"\d+", "", _name) # one number + + metadata = {} + # add knob to metadata + for knob in ["lookup", "length"]: + value = node[knob].value() + animated = node[knob].isAnimated() + if animated: + value = [ + ((node[knob].getValueAt(i)) - i) + for i in range( + track_item.timelineIn(), track_item.timelineOut() + 1) + ] + + metadata[knob] = value + + # make effect + otio_effect = otio.schema.TimeEffect() + otio_effect.name = name + otio_effect.effect_name = effect_name + otio_effect.metadata = metadata + + # add otio effect to clip effects + otio_clip.effects.append(otio_effect) + + +def create_otio_reference(clip): + metadata = _get_metadata(clip) + media_source = clip.mediaSource() + + # get file info for path and start frame + file_info = media_source.fileinfos().pop() + frame_start = file_info.startFrame() + path = file_info.filename() + + # get padding and other file infos + padding = media_source.filenamePadding() + file_head = media_source.filenameHead() + is_sequence = not media_source.singleFile() + frame_duration = media_source.duration() + fps = utils.get_rate(clip) or self.project_fps + extension = os.path.splitext(path)[-1] + + if is_sequence: + metadata.update({ + "isSequence": True, + "padding": padding + }) + + # add resolution metadata + metadata.update({ + "openpype.source.colourtransform": clip.sourceMediaColourTransform(), + "openpype.source.width": int(media_source.width()), + "openpype.source.height": int(media_source.height()), + "openpype.source.pixelAspect": float(media_source.pixelAspect()) + }) + + otio_ex_ref_item = None + + if is_sequence: + # if it is file sequence try to create `ImageSequenceReference` + # the OTIO might not be compatible so return nothing and do it old way + try: + dirname = os.path.dirname(path) + otio_ex_ref_item = otio.schema.ImageSequenceReference( + target_url_base=dirname + os.sep, + name_prefix=file_head, + name_suffix=extension, + start_frame=frame_start, + frame_zero_padding=padding, + rate=fps, + available_range=create_otio_time_range( + frame_start, + frame_duration, + fps + ) + ) + except AttributeError: + pass + + if not otio_ex_ref_item: + reformat_path = utils.get_reformated_path(path, padded=False) + # in case old OTIO or video file create `ExternalReference` + otio_ex_ref_item = otio.schema.ExternalReference( + target_url=reformat_path, + available_range=create_otio_time_range( + frame_start, + frame_duration, + fps + ) + ) + + # add metadata to otio item + add_otio_metadata(otio_ex_ref_item, media_source, **metadata) + + return otio_ex_ref_item + + +def get_marker_color(tag): + icon = tag.icon() + pat = r'icons:Tag(?P\w+)\.\w+' + + res = re.search(pat, icon) + if res: + color = res.groupdict().get('color') + if color.lower() in self.marker_color_map: + return self.marker_color_map[color.lower()] + + return otio.schema.MarkerColor.RED + + +def create_otio_markers(otio_item, item): + for tag in item.tags(): + if not tag.visible(): + continue + + if tag.name() == 'Copy': + # Hiero adds this tag to a lot of clips + continue + + frame_rate = utils.get_rate(item) or self.project_fps + + marked_range = otio.opentime.TimeRange( + start_time=otio.opentime.RationalTime( + tag.inTime(), + frame_rate + ), + duration=otio.opentime.RationalTime( + int(tag.metadata().dict().get('tag.length', '0')), + frame_rate + ) + ) + # add tag metadata but remove "tag." string + metadata = {} + + for key, value in tag.metadata().dict().items(): + _key = key.replace("tag.", "") + + try: + # capture exceptions which are related to strings only + _value = ast.literal_eval(value) + except (ValueError, SyntaxError): + _value = value + + metadata.update({_key: _value}) + + # Store the source item for future import assignment + metadata['hiero_source_type'] = item.__class__.__name__ + + marker = otio.schema.Marker( + name=tag.name(), + color=get_marker_color(tag), + marked_range=marked_range, + metadata=metadata + ) + + otio_item.markers.append(marker) + + +def create_otio_clip(track_item): + clip = track_item.source() + speed = track_item.playbackSpeed() + # flip if speed is in minus + source_in = track_item.sourceIn() if speed > 0 else track_item.sourceOut() + + duration = int(track_item.duration()) + + fps = utils.get_rate(track_item) or self.project_fps + name = track_item.name() + + media_reference = create_otio_reference(clip) + source_range = create_otio_time_range( + int(source_in), + int(duration), + fps + ) + + otio_clip = otio.schema.Clip( + name=name, + source_range=source_range, + media_reference=media_reference + ) + + # Add tags as markers + if self.include_tags: + create_otio_markers(otio_clip, track_item) + create_otio_markers(otio_clip, track_item.source()) + + # only if video + if not clip.mediaSource().hasAudio(): + # Add effects to clips + create_time_effects(otio_clip, track_item) + + return otio_clip + + +def create_otio_gap(gap_start, clip_start, tl_start_frame, fps): + return otio.schema.Gap( + source_range=create_otio_time_range( + gap_start, + (clip_start - tl_start_frame) - gap_start, + fps + ) + ) + + +def _create_otio_timeline(): + project = get_current_hiero_project(remove_untitled=False) + metadata = _get_metadata(self.timeline) + + metadata.update({ + "openpype.timeline.width": int(self.timeline.format().width()), + "openpype.timeline.height": int(self.timeline.format().height()), + "openpype.timeline.pixelAspect": int(self.timeline.format().pixelAspect()), # noqa + "openpype.project.useOCIOEnvironmentOverride": project.useOCIOEnvironmentOverride(), # noqa + "openpype.project.lutSetting16Bit": project.lutSetting16Bit(), + "openpype.project.lutSetting8Bit": project.lutSetting8Bit(), + "openpype.project.lutSettingFloat": project.lutSettingFloat(), + "openpype.project.lutSettingLog": project.lutSettingLog(), + "openpype.project.lutSettingViewer": project.lutSettingViewer(), + "openpype.project.lutSettingWorkingSpace": project.lutSettingWorkingSpace(), # noqa + "openpype.project.lutUseOCIOForExport": project.lutUseOCIOForExport(), + "openpype.project.ocioConfigName": project.ocioConfigName(), + "openpype.project.ocioConfigPath": project.ocioConfigPath() + }) + + start_time = create_otio_rational_time( + self.timeline.timecodeStart(), self.project_fps) + + return otio.schema.Timeline( + name=self.timeline.name(), + global_start_time=start_time, + metadata=metadata + ) + + +def create_otio_track(track_type, track_name): + return otio.schema.Track( + name=track_name, + kind=self.track_types[track_type] + ) + + +def add_otio_gap(track_item, otio_track, prev_out): + gap_length = track_item.timelineIn() - prev_out + if prev_out != 0: + gap_length -= 1 + + gap = otio.opentime.TimeRange( + duration=otio.opentime.RationalTime( + gap_length, + self.project_fps + ) + ) + otio_gap = otio.schema.Gap(source_range=gap) + otio_track.append(otio_gap) + + +def add_otio_metadata(otio_item, media_source, **kwargs): + metadata = _get_metadata(media_source) + + # add additional metadata from kwargs + if kwargs: + metadata.update(kwargs) + + # add metadata to otio item metadata + for key, value in metadata.items(): + otio_item.metadata.update({key: value}) + + +def create_otio_timeline(): + + def set_prev_item(itemindex, track_item): + # Add Gap if needed + if itemindex == 0: + # if it is first track item at track then add + # it to previouse item + return track_item + + else: + # get previouse item + return track_item.parent().items()[itemindex - 1] + + # get current timeline + self.timeline = hiero.ui.activeSequence() + self.project_fps = self.timeline.framerate().toFloat() + + # convert timeline to otio + otio_timeline = _create_otio_timeline() + + # loop all defined track types + for track in self.timeline.items(): + # skip if track is disabled + if not track.isEnabled(): + continue + + # convert track to otio + otio_track = create_otio_track( + type(track), track.name()) + + for itemindex, track_item in enumerate(track): + # Add Gap if needed + if itemindex == 0: + # if it is first track item at track then add + # it to previouse item + prev_item = track_item + + else: + # get previouse item + prev_item = track_item.parent().items()[itemindex - 1] + + # calculate clip frame range difference from each other + clip_diff = track_item.timelineIn() - prev_item.timelineOut() + + # add gap if first track item is not starting + # at first timeline frame + if itemindex == 0 and track_item.timelineIn() > 0: + add_otio_gap(track_item, otio_track, 0) + + # or add gap if following track items are having + # frame range differences from each other + elif itemindex and clip_diff != 1: + add_otio_gap(track_item, otio_track, prev_item.timelineOut()) + + # create otio clip and add it to track + otio_clip = create_otio_clip(track_item) + otio_track.append(otio_clip) + + # Add tags as markers + if self.include_tags: + create_otio_markers(otio_track, track) + + # add track to otio timeline + otio_timeline.tracks.append(otio_track) + + return otio_timeline + + +def write_to_file(otio_timeline, path): + otio.adapters.write_to_file(otio_timeline, path) diff --git a/openpype/hosts/flame/otio/utils.py b/openpype/hosts/flame/otio/utils.py new file mode 100644 index 00000000000..4c5d46bd51d --- /dev/null +++ b/openpype/hosts/flame/otio/utils.py @@ -0,0 +1,80 @@ +import re +import opentimelineio as otio + + +def timecode_to_frames(timecode, framerate): + rt = otio.opentime.from_timecode(timecode, 24) + return int(otio.opentime.to_frames(rt)) + + +def frames_to_timecode(frames, framerate): + rt = otio.opentime.from_frames(frames, framerate) + return otio.opentime.to_timecode(rt) + + +def frames_to_secons(frames, framerate): + rt = otio.opentime.from_frames(frames, framerate) + return otio.opentime.to_seconds(rt) + + +def get_reformated_path(path, padded=True): + """ + Return fixed python expression path + + Args: + path (str): path url or simple file name + + Returns: + type: string with reformated path + + Example: + get_reformated_path("plate.[0001-1008].exr") > plate.%04d.exr + + """ + if "%" in path: + padding_pattern = r"(\d+)" + padding = int(re.findall(padding_pattern, path).pop()) + num_pattern = r"(%\d+d)" + if padded: + path = re.sub(num_pattern, "%0{}d".format(padding), path) + else: + path = re.sub(num_pattern, "%d", path) + return path + + +def get_padding_from_path(path): + """ + Return padding number from DaVinci Resolve sequence path style + + Args: + path (str): path url or simple file name + + Returns: + int: padding number + + Example: + get_padding_from_path("plate.[0001-1008].exr") > 4 + + """ + padding_pattern = "(\\d+)(?=-)" + if "[" in path: + return len(re.findall(padding_pattern, path).pop()) + + return None + + +def get_rate(item): + if not hasattr(item, 'framerate'): + return None + + num, den = item.framerate().toRational() + + try: + rate = float(num) / float(den) + except ZeroDivisionError: + return None + + if rate.is_integer(): + return rate + + return round(rate, 4) From f38e4ce44ca09b7390aa4380671f38f14d3c0e8a Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 22 Oct 2021 11:26:21 +0200 Subject: [PATCH 002/492] Flame: testing passing selection from timeline --- openpype/hosts/flame/__init__.py | 2 ++ openpype/hosts/flame/api/menu.py | 15 ++++++++------- .../plugins/publish/collect_test_selection.py | 15 +++++++++++++++ 3 files changed, 25 insertions(+), 7 deletions(-) create mode 100644 openpype/hosts/flame/plugins/publish/collect_test_selection.py diff --git a/openpype/hosts/flame/__init__.py b/openpype/hosts/flame/__init__.py index dc3d3e7cbaa..28b7aa8ea53 100644 --- a/openpype/hosts/flame/__init__.py +++ b/openpype/hosts/flame/__init__.py @@ -51,6 +51,7 @@ app_framework = None apps = [] +selection = None __all__ = [ @@ -65,6 +66,7 @@ "app_framework", "apps", + "selection", # pipeline "install", diff --git a/openpype/hosts/flame/api/menu.py b/openpype/hosts/flame/api/menu.py index 65d1535bebc..d6788f3f52a 100644 --- a/openpype/hosts/flame/api/menu.py +++ b/openpype/hosts/flame/api/menu.py @@ -7,7 +7,6 @@ from .lib import rescan_hooks from openpype.tools.utils.host_tools import HostToolsHelper - menu_group_name = 'OpenPype' default_flame_export_presets = { @@ -16,6 +15,10 @@ 'Thumbnail': {'PresetVisibility': 3, 'PresetType': 0, 'PresetFile': 'Generate Thumbnail.xml'} } +def send_selection(selection): + import openpype.hosts.flame as opflame + opflame.selection = selection + print(opflame.selection) class _FlameMenuApp(object): def __init__(self, framework): @@ -99,10 +102,12 @@ def build_menu(self): }) menu['actions'].append({ "name": "Create ...", + "isVisible": send_selection, "execute": lambda x: self.tools_helper.show_creator() }) menu['actions'].append({ "name": "Publish ...", + "isVisible": send_selection, "execute": lambda x: self.tools_helper.show_publish() }) menu['actions'].append({ @@ -119,9 +124,6 @@ def build_menu(self): }) return menu - def get_projects(self, *args, **kwargs): - pass - def refresh(self, *args, **kwargs): self.rescan() @@ -163,10 +165,12 @@ def build_menu(self): menu['actions'].append({ "name": "Create ...", + "isVisible": send_selection, "execute": lambda x: self.tools_helper.show_creator() }) menu['actions'].append({ "name": "Publish ...", + "isVisible": send_selection, "execute": lambda x: self.tools_helper.show_publish() }) menu['actions'].append({ @@ -180,9 +184,6 @@ def build_menu(self): return menu - def get_projects(self, *args, **kwargs): - pass - def refresh(self, *args, **kwargs): self.rescan() diff --git a/openpype/hosts/flame/plugins/publish/collect_test_selection.py b/openpype/hosts/flame/plugins/publish/collect_test_selection.py new file mode 100644 index 00000000000..6534894f2e1 --- /dev/null +++ b/openpype/hosts/flame/plugins/publish/collect_test_selection.py @@ -0,0 +1,15 @@ +import pyblish.api +import openpype.hosts.flame as opflame +import flame + +@pyblish.api.log +class CollectTestSelection(pyblish.api.ContextPlugin): + """testing selection sharing + """ + + order = pyblish.api.CollectorOrder + label = "test selection" + hosts = ["flame"] + + def process(self, context): + self.log.info(opflame.selection) \ No newline at end of file From fabfaac3f56df68e7096402f9223623ce6e35e58 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 16 Nov 2021 09:43:42 +0100 Subject: [PATCH 003/492] OP-2019 - extracted headless_publish function to lib --- openpype/lib/remote_publish.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/openpype/lib/remote_publish.py b/openpype/lib/remote_publish.py index f7d7955b794..79b130913f3 100644 --- a/openpype/lib/remote_publish.py +++ b/openpype/lib/remote_publish.py @@ -11,6 +11,21 @@ from openpype.lib.plugin_tools import parse_json +def headless_publish(log): + """Runs publish in a opened host with a context and closes Python process. + + Host is being closed via ClosePS pyblish plugin which triggers 'exit' + method in ConsoleTrayApp. + """ + dbcon = get_webpublish_conn() + _id = os.environ.get("BATCH_LOG_ID") + if not _id: + log.warning("Unable to store log records, batch will be unfinished!") + return + + publish_and_log(dbcon, _id, log, 'CloseAE') + + def get_webpublish_conn(): """Get connection to OP 'webpublishes' collection.""" mongo_client = OpenPypeMongoConnection.get_mongo_client() From 939eabfc41f6e284c826cd225fc718865bf952cf Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 16 Nov 2021 09:44:16 +0100 Subject: [PATCH 004/492] OP-2019 - remote publish needs to have order of methods flipped --- .../hosts/aftereffects/plugins/publish/extract_local_render.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/hosts/aftereffects/plugins/publish/extract_local_render.py b/openpype/hosts/aftereffects/plugins/publish/extract_local_render.py index 37337e7fee5..b36ab24bde9 100644 --- a/openpype/hosts/aftereffects/plugins/publish/extract_local_render.py +++ b/openpype/hosts/aftereffects/plugins/publish/extract_local_render.py @@ -19,10 +19,9 @@ def process(self, instance): staging_dir = instance.data["stagingDir"] self.log.info("staging_dir::{}".format(staging_dir)) - stub.render(staging_dir) - # pull file name from Render Queue Output module render_q = stub.get_render_info() + stub.render(staging_dir) if not render_q: raise ValueError("No file extension set in Render Queue") _, ext = os.path.splitext(os.path.basename(render_q.file_name)) From 6b45101ed087e8a49d59a8efa01477a6e725a120 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 16 Nov 2021 09:44:49 +0100 Subject: [PATCH 005/492] OP-2019 - added test classes for remote publish --- .../aftereffects/plugins/publish/closeAE.py | 29 ++++++ .../test_publish_in_aftereffects.py | 96 +++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 openpype/hosts/aftereffects/plugins/publish/closeAE.py create mode 100644 tests/integration/hosts/aftereffects/test_publish_in_aftereffects.py diff --git a/openpype/hosts/aftereffects/plugins/publish/closeAE.py b/openpype/hosts/aftereffects/plugins/publish/closeAE.py new file mode 100644 index 00000000000..e6e96234743 --- /dev/null +++ b/openpype/hosts/aftereffects/plugins/publish/closeAE.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +"""Close AE after publish. For Webpublishing only.""" +import os + +import pyblish.api + +from avalon import aftereffects + + +class CloseAE(pyblish.api.ContextPlugin): + """Close AE after publish. For Webpublishing only. + """ + + order = pyblish.api.IntegratorOrder + 14 + label = "Close AE" + optional = True + active = True + + hosts = ["aftereffects"] + targets = ["remotepublish"] + + def process(self, context): + self.log.info("CloseAE") + + stub = aftereffects.stub() + self.log.info("Shutting down AE") + stub.save() + stub.close() + self.log.info("AE closed") diff --git a/tests/integration/hosts/aftereffects/test_publish_in_aftereffects.py b/tests/integration/hosts/aftereffects/test_publish_in_aftereffects.py new file mode 100644 index 00000000000..f709e401203 --- /dev/null +++ b/tests/integration/hosts/aftereffects/test_publish_in_aftereffects.py @@ -0,0 +1,96 @@ +import pytest +import os +import shutil + +from tests.lib.testing_classes import PublishTest + + +class TestPublishInAfterEffects(PublishTest): + """Basic test case for publishing in AfterEffects + + Uses generic TestCase to prepare fixtures for test data, testing DBs, + env vars. + + Opens AfterEffects, run publish on prepared workile. + + Then checks content of DB (if subset, version, representations were + created. + Checks tmp folder if all expected files were published. + + """ + PERSIST = True + + TEST_FILES = [ + ("1qsrq6OJWVpOeXE2LTWrdbsLqEVu155Uf", + "test_aftereffects_publish.zip", + "") + ] + + APP = "aftereffects" + APP_VARIANT = "2021" + + APP_NAME = "{}/{}".format(APP, APP_VARIANT) + + TIMEOUT = 120 # publish timeout + + @pytest.fixture(scope="module") + def last_workfile_path(self, download_test_data): + """Get last_workfile_path from source data. + + Maya expects workfile in proper folder, so copy is done first. + """ + src_path = os.path.join(download_test_data, + "input", + "workfile", + "test_project_test_asset_TestTask_v001.aep") + dest_folder = os.path.join(download_test_data, + self.PROJECT, + self.ASSET, + "work", + self.TASK) + os.makedirs(dest_folder) + dest_path = os.path.join(dest_folder, + "test_project_test_asset_TestTask_v001.aep") + shutil.copy(src_path, dest_path) + + yield dest_path + + @pytest.fixture(scope="module") + def startup_scripts(self, monkeypatch_session, download_test_data): + """Points AfterEffects to userSetup file from input data""" + os.environ["HEADLESS_PUBLISH"] = "true" + + def test_db_asserts(self, dbcon, publish_finished): + """Host and input data dependent expected results in DB.""" + print("test_db_asserts") + assert 5 == dbcon.count_documents({"type": "version"}), \ + "Not expected no of versions" + + assert 0 == dbcon.count_documents({"type": "version", + "name": {"$ne": 1}}), \ + "Only versions with 1 expected" + + assert 1 == dbcon.count_documents({"type": "subset", + "name": "modelMain"}), \ + "modelMain subset must be present" + + assert 1 == dbcon.count_documents({"type": "subset", + "name": "workfileTest_task"}), \ + "workfileTest_task subset must be present" + + assert 11 == dbcon.count_documents({"type": "representation"}), \ + "Not expected no of representations" + + assert 2 == dbcon.count_documents({"type": "representation", + "context.subset": "modelMain", + "context.ext": "abc"}), \ + "Not expected no of representations with ext 'abc'" + + assert 2 == dbcon.count_documents({"type": "representation", + "context.subset": "modelMain", + "context.ext": "ma"}), \ + "Not expected no of representations with ext 'abc'" + + +if __name__ == "__main__": + test_case = TestPublishInAfterEffects() From af1a06c00052743f928cbe67d75ce448bc86fe6d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 18 Nov 2021 18:02:06 +0100 Subject: [PATCH 006/492] OP-2019 - bump up version of PS --- tests/integration/hosts/photoshop/test_publish_in_photoshop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/hosts/photoshop/test_publish_in_photoshop.py b/tests/integration/hosts/photoshop/test_publish_in_photoshop.py index 396468a9661..3fdfce7cc05 100644 --- a/tests/integration/hosts/photoshop/test_publish_in_photoshop.py +++ b/tests/integration/hosts/photoshop/test_publish_in_photoshop.py @@ -25,7 +25,7 @@ class TestPublishInPhotoshop(PublishTest): ] APP = "photoshop" - APP_VARIANT = "2020" + APP_VARIANT = "2021" APP_NAME = "{}/{}".format(APP, APP_VARIANT) From 04fd46f9e91e6b42089957581c25b18ad51062b0 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 18 Nov 2021 18:02:42 +0100 Subject: [PATCH 007/492] OP-2019 - added AE 2022 --- .../defaults/system_settings/applications.json | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/openpype/settings/defaults/system_settings/applications.json b/openpype/settings/defaults/system_settings/applications.json index cc80a94d3fd..8c119658bed 100644 --- a/openpype/settings/defaults/system_settings/applications.json +++ b/openpype/settings/defaults/system_settings/applications.json @@ -1098,6 +1098,23 @@ "linux": [] }, "environment": {} + }, + "2022": { + "enabled": true, + "variant_label": "2022", + "executables": { + "windows": [ + "C:\\Program Files\\Adobe\\Adobe After Effects 2022\\Support Files\\AfterFX.exe" + ], + "darwin": [], + "linux": [] + }, + "arguments": { + "windows": [], + "darwin": [], + "linux": [] + }, + "environment": {} } } }, From 1369098454ac40bc474283706e6508d756dd8bcb Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 18 Nov 2021 18:07:07 +0100 Subject: [PATCH 008/492] OP-2019 - added IS_TEST env var Used to differentiate between regular REMOTE_PUBLISH and automatic tests --- tests/integration/hosts/photoshop/test_publish_in_photoshop.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/integration/hosts/photoshop/test_publish_in_photoshop.py b/tests/integration/hosts/photoshop/test_publish_in_photoshop.py index 3fdfce7cc05..f8be8599ee3 100644 --- a/tests/integration/hosts/photoshop/test_publish_in_photoshop.py +++ b/tests/integration/hosts/photoshop/test_publish_in_photoshop.py @@ -57,6 +57,7 @@ def last_workfile_path(self, download_test_data): def startup_scripts(self, monkeypatch_session, download_test_data): """Points Maya to userSetup file from input data""" os.environ["IS_HEADLESS"] = "true" + os.environ["IS_TEST"] = "true" def test_db_asserts(self, dbcon, publish_finished): """Host and input data dependent expected results in DB.""" From 30e39bebabc7352bcc998850931a58b7bc95ec65 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 18 Nov 2021 18:15:08 +0100 Subject: [PATCH 009/492] OP-2019 - removed setting env vars in class Necessary env vars should be configured in testing zip file --- tests/integration/hosts/photoshop/test_publish_in_photoshop.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/integration/hosts/photoshop/test_publish_in_photoshop.py b/tests/integration/hosts/photoshop/test_publish_in_photoshop.py index f8be8599ee3..b634d422f34 100644 --- a/tests/integration/hosts/photoshop/test_publish_in_photoshop.py +++ b/tests/integration/hosts/photoshop/test_publish_in_photoshop.py @@ -56,8 +56,7 @@ def last_workfile_path(self, download_test_data): @pytest.fixture(scope="module") def startup_scripts(self, monkeypatch_session, download_test_data): """Points Maya to userSetup file from input data""" - os.environ["IS_HEADLESS"] = "true" - os.environ["IS_TEST"] = "true" + pass def test_db_asserts(self, dbcon, publish_finished): """Host and input data dependent expected results in DB.""" From 35153fca3ae5d83a64334f9bda14bcd40e011a60 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 18 Nov 2021 18:16:56 +0100 Subject: [PATCH 010/492] OP-2019 - added working test case for After Effects --- .../hosts/aftereffects/test_publish_in_aftereffects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/hosts/aftereffects/test_publish_in_aftereffects.py b/tests/integration/hosts/aftereffects/test_publish_in_aftereffects.py index f709e401203..d4e88dfd4c5 100644 --- a/tests/integration/hosts/aftereffects/test_publish_in_aftereffects.py +++ b/tests/integration/hosts/aftereffects/test_publish_in_aftereffects.py @@ -58,7 +58,7 @@ def last_workfile_path(self, download_test_data): @pytest.fixture(scope="module") def startup_scripts(self, monkeypatch_session, download_test_data): """Points AfterEffects to userSetup file from input data""" - os.environ["HEADLESS_PUBLISH"] = "true" + pass def test_db_asserts(self, dbcon, publish_finished): """Host and input data dependent expected results in DB.""" From 7456d0c3805f7d26e867ba46a08c75ccef97e632 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 18 Nov 2021 18:17:21 +0100 Subject: [PATCH 011/492] OP-2019 - added remote publishing method for automatic tests --- openpype/lib/remote_publish.py | 45 ++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/openpype/lib/remote_publish.py b/openpype/lib/remote_publish.py index 79b130913f3..3483898af73 100644 --- a/openpype/lib/remote_publish.py +++ b/openpype/lib/remote_publish.py @@ -11,19 +11,23 @@ from openpype.lib.plugin_tools import parse_json -def headless_publish(log): +def headless_publish(log, close_plugin_name=None, is_test=False): """Runs publish in a opened host with a context and closes Python process. Host is being closed via ClosePS pyblish plugin which triggers 'exit' method in ConsoleTrayApp. """ - dbcon = get_webpublish_conn() - _id = os.environ.get("BATCH_LOG_ID") - if not _id: - log.warning("Unable to store log records, batch will be unfinished!") - return + if not is_test: + dbcon = get_webpublish_conn() + _id = os.environ.get("BATCH_LOG_ID") + if not _id: + log.warning("Unable to store log records, " + "batch will be unfinished!") + return - publish_and_log(dbcon, _id, log, 'CloseAE') + publish_and_log(dbcon, _id, log, close_plugin_name) + else: + publish(log, 'CloseAE') def get_webpublish_conn(): @@ -51,6 +55,33 @@ def start_webpublish_log(dbcon, batch_id, user): }).inserted_id +def publish(log, close_plugin_name=None): + """Loops through all plugins, logs to console. Used for tests. + + Args: + log (OpenPypeLogger) + close_plugin_name (str): name of plugin with responsibility to + close host app + """ + # Error exit as soon as any error occurs. + error_format = "Failed {plugin.__name__}: {error} -- {error.traceback}" + + close_plugin = _get_close_plugin(close_plugin_name, log) + + for result in pyblish.util.publish_iter(): + for record in result["records"]: + log.info("{}: {}".format( + result["plugin"].label, record.msg)) + + if result["error"]: + log.error(error_format.format(**result)) + uninstall() + if close_plugin: # close host app explicitly after error + context = pyblish.api.Context() + close_plugin().process(context) + sys.exit(1) + + def publish_and_log(dbcon, _id, log, close_plugin_name=None): """Loops through all plugins, logs ok and fails into OP DB. From bcdca93cacbcc015d7b09babf3d23ca446b5cf8a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 18 Nov 2021 18:17:56 +0100 Subject: [PATCH 012/492] OP-2019 - removed unwanted host --- openpype/hooks/pre_foundry_apps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hooks/pre_foundry_apps.py b/openpype/hooks/pre_foundry_apps.py index 7df1a6a833f..85f68c6b604 100644 --- a/openpype/hooks/pre_foundry_apps.py +++ b/openpype/hooks/pre_foundry_apps.py @@ -13,7 +13,7 @@ class LaunchFoundryAppsWindows(PreLaunchHook): # Should be as last hook because must change launch arguments to string order = 1000 - app_groups = ["nuke", "nukex", "hiero", "nukestudio", "photoshop"] + app_groups = ["nuke", "nukex", "hiero", "nukestudio"] platforms = ["windows"] def execute(self): From 1154f61ac1a0d9382b3f9cd4304d77e2be9c65da Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 19 Nov 2021 12:26:28 +0100 Subject: [PATCH 013/492] OP-2019 - added details into documentation --- tests/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/README.md b/tests/README.md index 6317b2ab3c2..d0578f8059c 100644 --- a/tests/README.md +++ b/tests/README.md @@ -14,12 +14,12 @@ How to run: ---------- - single test class could be run by PyCharm and its pytest runner directly - OR -- use Openpype command 'runtests' from command line --- `${OPENPYPE_ROOT}/start.py runtests` +- use Openpype command 'runtests' from command line (`.venv` in ${OPENPYPE_ROOT} must be activated to use configured Python!) +-- `${OPENPYPE_ROOT}/python start.py runtests` By default, this command will run all tests in ${OPENPYPE_ROOT}/tests. Specific location could be provided to this command as an argument, either as absolute path, or relative path to ${OPENPYPE_ROOT}. -(eg. `${OPENPYPE_ROOT}/start.py runtests ../tests/integration`) will trigger only tests in `integration` folder. +(eg. `${OPENPYPE_ROOT}/python start.py runtests ../tests/integration`) will trigger only tests in `integration` folder. See `${OPENPYPE_ROOT}/cli.py:runtests` for other arguments. From fbb9d3e23279b6725a3d326ca1a93928c53cdedd Mon Sep 17 00:00:00 2001 From: "clement.hector" Date: Mon, 22 Nov 2021 13:58:51 +0100 Subject: [PATCH 014/492] Add parent asset in anatomy --- openpype/lib/avalon_context.py | 5 +++++ .../plugins/publish/collect_anatomy_context_data.py | 5 +++++ openpype/tools/workfiles/app.py | 11 ++++++++++- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/openpype/lib/avalon_context.py b/openpype/lib/avalon_context.py index 372e116f437..581e4b9dbda 100644 --- a/openpype/lib/avalon_context.py +++ b/openpype/lib/avalon_context.py @@ -486,6 +486,10 @@ def get_workdir_data(project_doc, asset_doc, task_name, host_name): project_task_types = project_doc["config"]["tasks"] task_code = project_task_types.get(task_type, {}).get("short_name") + parent = project_doc["name"] + if len(asset_doc["data"]["parents"]) != 0: + parent = asset_doc["data"]["parents"][-1] + data = { "project": { "name": project_doc["name"], @@ -497,6 +501,7 @@ def get_workdir_data(project_doc, asset_doc, task_name, host_name): "short": task_code, }, "asset": asset_doc["name"], + "parent": parent, "app": host_name, "user": getpass.getuser(), "hierarchy": hierarchy, diff --git a/openpype/plugins/publish/collect_anatomy_context_data.py b/openpype/plugins/publish/collect_anatomy_context_data.py index 6b95979b76f..b0c9eea5766 100644 --- a/openpype/plugins/publish/collect_anatomy_context_data.py +++ b/openpype/plugins/publish/collect_anatomy_context_data.py @@ -60,12 +60,17 @@ def process(self, context): project_task_types = project_entity["config"]["tasks"] task_code = project_task_types.get(task_type, {}).get("short_name") + parent = project_entity["name"] + if len(asset_entity["data"]["parents"]) != 0: + parent = asset_entity["data"]["parents"][-1] + context_data = { "project": { "name": project_entity["name"], "code": project_entity["data"].get("code") }, "asset": asset_entity["name"], + "parent": parent, "hierarchy": hierarchy.replace("\\", "/"), "task": { "name": task_name, diff --git a/openpype/tools/workfiles/app.py b/openpype/tools/workfiles/app.py index a4b1717a1cd..4253f7450a1 100644 --- a/openpype/tools/workfiles/app.py +++ b/openpype/tools/workfiles/app.py @@ -68,12 +68,16 @@ def __init__(self, parent, root, anatomy, template_key, session=None): "config.tasks": True, } ) + asset_doc = io.find_one( { "type": "asset", "name": asset_name }, - {"data.tasks": True} + { + "data.tasks": True, + "data.parents": True + } ) task_type = asset_doc["data"]["tasks"].get(task_name, {}).get("type") @@ -81,6 +85,10 @@ def __init__(self, parent, root, anatomy, template_key, session=None): project_task_types = project_doc["config"]["tasks"] task_short = project_task_types.get(task_type, {}).get("short_name") + parent = project_doc["name"] + if len(asset_doc["data"]["parents"]) != 0: + parent = asset_doc["data"]["parents"][-1] + self.data = { "project": { "name": project_doc["name"], @@ -92,6 +100,7 @@ def __init__(self, parent, root, anatomy, template_key, session=None): "type": task_type, "short": task_short, }, + "parent": parent, "version": 1, "user": getpass.getuser(), "comment": "", From 41a02fddc4b6241602f01620b3756d2887be9e54 Mon Sep 17 00:00:00 2001 From: "clement.hector" Date: Fri, 29 Oct 2021 11:34:26 +0200 Subject: [PATCH 015/492] add parent asset to doc --- website/docs/admin_settings_project_anatomy.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/website/docs/admin_settings_project_anatomy.md b/website/docs/admin_settings_project_anatomy.md index 30784686e26..e897b0ffab9 100644 --- a/website/docs/admin_settings_project_anatomy.md +++ b/website/docs/admin_settings_project_anatomy.md @@ -57,9 +57,14 @@ We have a few required anatomy templates for OpenPype to work properly, however | `project[code]` | Project's code | | `hierarchy` | All hierarchical parents as subfolders | | `asset` | Name of asset or shot | +<<<<<<< HEAD | `task[name]` | Name of task | | `task[type]` | Type of task | | `task[short]` | Shortname of task | +======= +| `parent` | Name of parent folder | +| `task` | Name of task | +>>>>>>> add7db0c0... add parent asset to doc | `version` | Version number | | `subset` | Subset name | | `family` | Main family name | From dfdc1798cff1c3e7af7f42eb1ea86102d697770c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Nov 2021 21:40:46 +0000 Subject: [PATCH 016/492] Bump algoliasearch-helper from 3.4.4 to 3.6.2 in /website Bumps [algoliasearch-helper](https://github.com/algolia/algoliasearch-helper-js) from 3.4.4 to 3.6.2. - [Release notes](https://github.com/algolia/algoliasearch-helper-js/releases) - [Changelog](https://github.com/algolia/algoliasearch-helper-js/blob/develop/CHANGELOG) - [Commits](https://github.com/algolia/algoliasearch-helper-js/compare/3.4.4...3.6.2) --- updated-dependencies: - dependency-name: algoliasearch-helper dependency-type: indirect ... Signed-off-by: dependabot[bot] --- website/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/website/yarn.lock b/website/yarn.lock index ae40005384f..89da2289de5 100644 --- a/website/yarn.lock +++ b/website/yarn.lock @@ -1961,9 +1961,9 @@ ajv@^6.1.0, ajv@^6.12.4, ajv@^6.12.5: uri-js "^4.2.2" algoliasearch-helper@^3.3.4: - version "3.4.4" - resolved "https://registry.yarnpkg.com/algoliasearch-helper/-/algoliasearch-helper-3.4.4.tgz#f2eb46bc4d2f6fed82c7201b8ac4ce0a1988ae67" - integrity sha512-OjyVLjykaYKCMxxRMZNiwLp8CS310E0qAeIY2NaublcmLAh8/SL19+zYHp7XCLtMem2ZXwl3ywMiA32O9jszuw== + version "3.6.2" + resolved "https://registry.yarnpkg.com/algoliasearch-helper/-/algoliasearch-helper-3.6.2.tgz#45e19b12589cfa0c611b573287f65266ea2cc14a" + integrity sha512-Xx0NOA6k4ySn+R2l3UMSONAaMkyfmrZ3AP1geEMo32MxDJQJesZABZYsldO9fa6FKQxH91afhi4hO1G0Zc2opg== dependencies: events "^1.1.1" From baba65d5e7b2a36e8363bc3ba0a269bb28f690fb Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 26 Nov 2021 15:39:06 +0100 Subject: [PATCH 017/492] use composite_images from 'tvpaint.lib' --- openpype/hosts/tvpaint/api/lib.py | 19 ------------------- .../plugins/publish/extract_sequence.py | 7 ++++--- 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/openpype/hosts/tvpaint/api/lib.py b/openpype/hosts/tvpaint/api/lib.py index 539cebe6468..8e63c9e5b25 100644 --- a/openpype/hosts/tvpaint/api/lib.py +++ b/openpype/hosts/tvpaint/api/lib.py @@ -4,25 +4,6 @@ from avalon.tvpaint.lib import execute_george -def composite_images(input_image_paths, output_filepath): - """Composite images in order from passed list. - - Raises: - ValueError: When entered list is empty. - """ - if not input_image_paths: - raise ValueError("Nothing to composite.") - - img_obj = None - for image_filepath in input_image_paths: - _img_obj = Image.open(image_filepath) - if img_obj is None: - img_obj = _img_obj - else: - img_obj.alpha_composite(_img_obj) - img_obj.save(output_filepath) - - def set_context_settings(asset_doc=None): """Set workfile settings by asset document data. diff --git a/openpype/hosts/tvpaint/plugins/publish/extract_sequence.py b/openpype/hosts/tvpaint/plugins/publish/extract_sequence.py index 6235b6211d9..31f685919ef 100644 --- a/openpype/hosts/tvpaint/plugins/publish/extract_sequence.py +++ b/openpype/hosts/tvpaint/plugins/publish/extract_sequence.py @@ -2,17 +2,18 @@ import copy import tempfile +from PIL import Image + import pyblish.api from avalon.tvpaint import lib -from openpype.hosts.tvpaint.api.lib import composite_images from openpype.hosts.tvpaint.lib import ( calculate_layers_extraction_data, get_frame_filename_template, fill_reference_frames, composite_rendered_layers, - rename_filepaths_by_frame_start + rename_filepaths_by_frame_start, + composite_images ) -from PIL import Image class ExtractSequence(pyblish.api.Extractor): From ced7ff99377a227cb0a4dcba62b3626a387ea511 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 26 Nov 2021 15:39:37 +0100 Subject: [PATCH 018/492] copied communication server to openpype --- .../hosts/tvpaint/api/communication_server.py | 939 ++++++++++++++++++ 1 file changed, 939 insertions(+) create mode 100644 openpype/hosts/tvpaint/api/communication_server.py diff --git a/openpype/hosts/tvpaint/api/communication_server.py b/openpype/hosts/tvpaint/api/communication_server.py new file mode 100644 index 00000000000..6c8aca5445b --- /dev/null +++ b/openpype/hosts/tvpaint/api/communication_server.py @@ -0,0 +1,939 @@ +import os +import json +import time +import subprocess +import collections +import asyncio +import logging +import socket +import platform +import filecmp +import tempfile +import threading +import shutil +from queue import Queue +from contextlib import closing + +from aiohttp import web +from aiohttp_json_rpc import JsonRpc +from aiohttp_json_rpc.protocol import ( + encode_request, encode_error, decode_msg, JsonRpcMsgTyp +) +from aiohttp_json_rpc.exceptions import RpcError + +from avalon import api +from openpype.hosts.tvpaint.tvpaint_plugin import get_plugin_files_path + +log = logging.getLogger(__name__) +log.setLevel(logging.DEBUG) + + +class CommunicationWrapper: + # TODO add logs and exceptions + communicator = None + + log = logging.getLogger("CommunicationWrapper") + + @classmethod + def create_qt_communicator(cls, *args, **kwargs): + """Create communicator for Artist usage.""" + communicator = QtCommunicator(*args, **kwargs) + cls.set_communicator(communicator) + return communicator + + @classmethod + def set_communicator(cls, communicator): + if not cls.communicator: + cls.communicator = communicator + else: + cls.log.warning("Communicator was set multiple times.") + + @classmethod + def client(cls): + if not cls.communicator: + return None + return cls.communicator.client() + + @classmethod + def execute_george(cls, george_script): + """Execute passed goerge script in TVPaint.""" + if not cls.communicator: + return + return cls.communicator.execute_george(george_script) + + +class WebSocketServer: + def __init__(self): + self.client = None + + self.loop = asyncio.new_event_loop() + self.app = web.Application(loop=self.loop) + self.port = self.find_free_port() + self.websocket_thread = WebsocketServerThread( + self, self.port, loop=self.loop + ) + + @property + def server_is_running(self): + return self.websocket_thread.server_is_running + + def add_route(self, *args, **kwargs): + self.app.router.add_route(*args, **kwargs) + + @staticmethod + def find_free_port(): + with closing( + socket.socket(socket.AF_INET, socket.SOCK_STREAM) + ) as sock: + sock.bind(("", 0)) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + port = sock.getsockname()[1] + return port + + def start(self): + self.websocket_thread.start() + + def stop(self): + try: + if self.websocket_thread.is_running: + log.debug("Stopping websocket server") + self.websocket_thread.is_running = False + self.websocket_thread.stop() + except Exception: + log.warning( + "Error has happened during Killing websocket server", + exc_info=True + ) + + +class WebsocketServerThread(threading.Thread): + """ Listener for websocket rpc requests. + + It would be probably better to "attach" this to main thread (as for + example Harmony needs to run something on main thread), but currently + it creates separate thread and separate asyncio event loop + """ + def __init__(self, module, port, loop): + super(WebsocketServerThread, self).__init__() + self.is_running = False + self.server_is_running = False + self.port = port + self.module = module + self.loop = loop + self.runner = None + self.site = None + self.tasks = [] + + def run(self): + self.is_running = True + + try: + log.debug("Starting websocket server") + + self.loop.run_until_complete(self.start_server()) + + log.info( + "Running Websocket server on URL:" + " \"ws://localhost:{}\"".format(self.port) + ) + + asyncio.ensure_future(self.check_shutdown(), loop=self.loop) + + self.server_is_running = True + self.loop.run_forever() + + except Exception: + log.warning( + "Websocket Server service has failed", exc_info=True + ) + finally: + self.server_is_running = False + # optional + self.loop.close() + + self.is_running = False + log.info("Websocket server stopped") + + async def start_server(self): + """ Starts runner and TCPsite """ + self.runner = web.AppRunner(self.module.app) + await self.runner.setup() + self.site = web.TCPSite(self.runner, "localhost", self.port) + await self.site.start() + + def stop(self): + """Sets is_running flag to false, 'check_shutdown' shuts server down""" + self.is_running = False + + async def check_shutdown(self): + """ Future that is running and checks if server should be running + periodically. + """ + while self.is_running: + while self.tasks: + task = self.tasks.pop(0) + log.debug("waiting for task {}".format(task)) + await task + log.debug("returned value {}".format(task.result)) + + await asyncio.sleep(0.5) + + log.debug("## Server shutdown started") + + await self.site.stop() + log.debug("# Site stopped") + await self.runner.cleanup() + log.debug("# Server runner stopped") + tasks = [ + task for task in asyncio.all_tasks() + if task is not asyncio.current_task() + ] + list(map(lambda task: task.cancel(), tasks)) # cancel all the tasks + results = await asyncio.gather(*tasks, return_exceptions=True) + log.debug(f"Finished awaiting cancelled tasks, results: {results}...") + await self.loop.shutdown_asyncgens() + # to really make sure everything else has time to stop + await asyncio.sleep(0.07) + self.loop.stop() + + +class BaseTVPaintRpc(JsonRpc): + def __init__(self, communication_obj, route_name="", **kwargs): + super().__init__(**kwargs) + self.requests_ids = collections.defaultdict(lambda: 0) + self.waiting_requests = collections.defaultdict(list) + self.responses = collections.defaultdict(list) + + self.route_name = route_name + self.communication_obj = communication_obj + + async def _handle_rpc_msg(self, http_request, raw_msg): + # This is duplicated code from super but there is no way how to do it + # to be able handle server->client requests + host = http_request.host + if host in self.waiting_requests: + try: + _raw_message = raw_msg.data + msg = decode_msg(_raw_message) + + except RpcError as error: + await self._ws_send_str(http_request, encode_error(error)) + return + + if msg.type in (JsonRpcMsgTyp.RESULT, JsonRpcMsgTyp.ERROR): + msg_data = json.loads(_raw_message) + if msg_data.get("id") in self.waiting_requests[host]: + self.responses[host].append(msg_data) + return + + return await super()._handle_rpc_msg(http_request, raw_msg) + + def client_connected(self): + # TODO This is poor check. Add check it is client from TVPaint + if self.clients: + return True + return False + + def send_notification(self, client, method, params=None): + if params is None: + params = [] + asyncio.run_coroutine_threadsafe( + client.ws.send_str(encode_request(method, params=params)), + loop=self.loop + ) + + def send_request(self, client, method, params=None, timeout=0): + if params is None: + params = [] + + client_host = client.host + + request_id = self.requests_ids[client_host] + self.requests_ids[client_host] += 1 + + self.waiting_requests[client_host].append(request_id) + + log.debug("Sending request to client {} ({}, {}) id: {}".format( + client_host, method, params, request_id + )) + future = asyncio.run_coroutine_threadsafe( + client.ws.send_str(encode_request(method, request_id, params)), + loop=self.loop + ) + result = future.result() + + not_found = object() + response = not_found + start = time.time() + while True: + if client.ws.closed: + return None + + for _response in self.responses[client_host]: + _id = _response.get("id") + if _id == request_id: + response = _response + break + + if response is not not_found: + break + + if timeout > 0 and (time.time() - start) > timeout: + raise Exception("Timeout passed") + return + + time.sleep(0.1) + + if response is not_found: + raise Exception("Connection closed") + + self.responses[client_host].remove(response) + + error = response.get("error") + result = response.get("result") + if error: + raise Exception("Error happened: {}".format(error)) + return result + + +class QtTVPaintRpc(BaseTVPaintRpc): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + from openpype.tools.utils import host_tools + self.tools_helper = host_tools.HostToolsHelper() + + route_name = self.route_name + + # Register methods + self.add_methods( + (route_name, self.workfiles_tool), + (route_name, self.loader_tool), + (route_name, self.creator_tool), + (route_name, self.subset_manager_tool), + (route_name, self.publish_tool), + (route_name, self.scene_inventory_tool), + (route_name, self.library_loader_tool), + (route_name, self.experimental_tools) + ) + + # Panel routes for tools + async def workfiles_tool(self): + log.info("Triggering Workfile tool") + item = MainThreadItem(self.tools_helper.show_workfiles) + self._execute_in_main_thread(item) + return + + async def loader_tool(self): + log.info("Triggering Loader tool") + item = MainThreadItem(self.tools_helper.show_loader) + self._execute_in_main_thread(item) + return + + async def creator_tool(self): + log.info("Triggering Creator tool") + item = MainThreadItem(self.tools_helper.show_creator) + await self._async_execute_in_main_thread(item, wait=False) + + async def subset_manager_tool(self): + log.info("Triggering Subset Manager tool") + item = MainThreadItem(self.tools_helper.show_subset_manager) + # Do not wait for result of callback + self._execute_in_main_thread(item, wait=False) + return + + async def publish_tool(self): + log.info("Triggering Publish tool") + item = MainThreadItem(self.tools_helper.show_publish) + self._execute_in_main_thread(item) + return + + async def scene_inventory_tool(self): + """Open Scene Inventory tool. + + Funciton can't confirm if tool was opened becauise one part of + SceneInventory initialization is calling websocket request to host but + host can't response because is waiting for response from this call. + """ + log.info("Triggering Scene inventory tool") + item = MainThreadItem(self.tools_helper.show_scene_inventory) + # Do not wait for result of callback + self._execute_in_main_thread(item, wait=False) + return + + async def library_loader_tool(self): + log.info("Triggering Library loader tool") + item = MainThreadItem(self.tools_helper.show_library_loader) + self._execute_in_main_thread(item) + return + + async def experimental_tools(self): + log.info("Triggering Library loader tool") + item = MainThreadItem(self.tools_helper.show_experimental_tools_dialog) + self._execute_in_main_thread(item) + return + + async def _async_execute_in_main_thread(self, item, **kwargs): + await self.communication_obj.async_execute_in_main_thread( + item, **kwargs + ) + + def _execute_in_main_thread(self, item, **kwargs): + return self.communication_obj.execute_in_main_thread(item, **kwargs) + + +class MainThreadItem: + """Structure to store information about callback in main thread. + + Item should be used to execute callback in main thread which may be needed + for execution of Qt objects. + + Item store callback (callable variable), arguments and keyword arguments + for the callback. Item hold information about it's process. + """ + not_set = object() + sleep_time = 0.1 + + def __init__(self, callback, *args, **kwargs): + self.done = False + self.exception = self.not_set + self.result = self.not_set + self.callback = callback + self.args = args + self.kwargs = kwargs + + def execute(self): + """Execute callback and store it's result. + + Method must be called from main thread. Item is marked as `done` + when callback execution finished. Store output of callback of exception + information when callback raise one. + """ + log.debug("Executing process in main thread") + if self.done: + log.warning("- item is already processed") + return + + callback = self.callback + args = self.args + kwargs = self.kwargs + log.info("Running callback: {}".format(str(callback))) + try: + result = callback(*args, **kwargs) + self.result = result + + except Exception as exc: + self.exception = exc + + finally: + self.done = True + + def wait(self): + """Wait for result from main thread. + + This method stops current thread until callback is executed. + + Returns: + object: Output of callback. May be any type or object. + + Raises: + Exception: Reraise any exception that happened during callback + execution. + """ + while not self.done: + time.sleep(self.sleep_time) + + if self.exception is self.not_set: + return self.result + raise self.exception + + async def async_wait(self): + """Wait for result from main thread. + + Returns: + object: Output of callback. May be any type or object. + + Raises: + Exception: Reraise any exception that happened during callback + execution. + """ + while not self.done: + await asyncio.sleep(self.sleep_time) + + if self.exception is self.not_set: + return self.result + raise self.exception + + +class BaseCommunicator: + def __init__(self): + self.process = None + self.websocket_server = None + self.websocket_rpc = None + self.exit_code = None + self._connected_client = None + + @property + def server_is_running(self): + if self.websocket_server is None: + return False + return self.websocket_server.server_is_running + + def _windows_file_process(self, src_dst_mapping, to_remove): + """Windows specific file processing asking for admin permissions. + + It is required to have administration permissions to modify plugin + files in TVPaint installation folder. + + Method requires `pywin32` python module. + + Args: + src_dst_mapping (list, tuple, set): Mapping of source file to + destination. Both must be full path. Each item must be iterable + of size 2 `(C:/src/file.dll, C:/dst/file.dll)`. + to_remove (list): Fullpath to files that should be removed. + """ + + import pythoncom + from win32comext.shell import shell + + # Create temp folder where plugin files are temporary copied + # - reason is that copy to TVPaint requires administartion permissions + # but admin may not have access to source folder + tmp_dir = os.path.normpath( + tempfile.mkdtemp(prefix="tvpaint_copy_") + ) + + # Copy source to temp folder and create new mapping + dst_folders = collections.defaultdict(list) + new_src_dst_mapping = [] + for old_src, dst in src_dst_mapping: + new_src = os.path.join(tmp_dir, os.path.split(old_src)[1]) + shutil.copy(old_src, new_src) + new_src_dst_mapping.append((new_src, dst)) + + for src, dst in new_src_dst_mapping: + src = os.path.normpath(src) + dst = os.path.normpath(dst) + dst_filename = os.path.basename(dst) + dst_folder_path = os.path.dirname(dst) + dst_folders[dst_folder_path].append((dst_filename, src)) + + # create an instance of IFileOperation + fo = pythoncom.CoCreateInstance( + shell.CLSID_FileOperation, + None, + pythoncom.CLSCTX_ALL, + shell.IID_IFileOperation + ) + # Add delete command to file operation object + for filepath in to_remove: + item = shell.SHCreateItemFromParsingName( + filepath, None, shell.IID_IShellItem + ) + fo.DeleteItem(item) + + # here you can use SetOperationFlags, progress Sinks, etc. + for folder_path, items in dst_folders.items(): + # create an instance of IShellItem for the target folder + folder_item = shell.SHCreateItemFromParsingName( + folder_path, None, shell.IID_IShellItem + ) + for _dst_filename, source_file_path in items: + # create an instance of IShellItem for the source item + copy_item = shell.SHCreateItemFromParsingName( + source_file_path, None, shell.IID_IShellItem + ) + # queue the copy operation + fo.CopyItem(copy_item, folder_item, _dst_filename, None) + + # commit + fo.PerformOperations() + + # Remove temp folder + shutil.rmtree(tmp_dir) + + def _prepare_windows_plugin(self, launch_args): + """Copy plugin to TVPaint plugins and set PATH to dependencies. + + Check if plugin in TVPaint's plugins exist and match to plugin + version to current implementation version. Based on 64-bit or 32-bit + version of the plugin. Path to libraries required for plugin is added + to PATH variable. + """ + + host_executable = launch_args[0] + executable_file = os.path.basename(host_executable) + if "64bit" in executable_file: + subfolder = "windows_x64" + elif "32bit" in executable_file: + subfolder = "windows_x86" + else: + raise ValueError( + "Can't determine if executable " + "leads to 32-bit or 64-bit TVPaint!" + ) + + plugin_files_path = get_plugin_files_path() + # Folder for right windows plugin files + source_plugins_dir = os.path.join(plugin_files_path, subfolder) + + # Path to libraies (.dll) required for plugin library + # - additional libraries can be copied to TVPaint installation folder + # (next to executable) or added to PATH environment variable + additional_libs_folder = os.path.join( + source_plugins_dir, + "additional_libraries" + ) + additional_libs_folder = additional_libs_folder.replace("\\", "/") + if additional_libs_folder not in os.environ["PATH"]: + os.environ["PATH"] += (os.pathsep + additional_libs_folder) + + # Path to TVPaint's plugins folder (where we want to add our plugin) + host_plugins_path = os.path.join( + os.path.dirname(host_executable), + "plugins" + ) + + # Files that must be copied to TVPaint's plugin folder + plugin_dir = os.path.join(source_plugins_dir, "plugin") + + to_copy = [] + to_remove = [] + # Remove old plugin name + deprecated_filepath = os.path.join( + host_plugins_path, "AvalonPlugin.dll" + ) + if os.path.exists(deprecated_filepath): + to_remove.append(deprecated_filepath) + + for filename in os.listdir(plugin_dir): + src_full_path = os.path.join(plugin_dir, filename) + dst_full_path = os.path.join(host_plugins_path, filename) + if dst_full_path in to_remove: + to_remove.remove(dst_full_path) + + if ( + not os.path.exists(dst_full_path) + or not filecmp.cmp(src_full_path, dst_full_path) + ): + to_copy.append((src_full_path, dst_full_path)) + + # Skip copy if everything is done + if not to_copy and not to_remove: + return + + # Try to copy + try: + self._windows_file_process(to_copy, to_remove) + except Exception: + log.error("Plugin copy failed", exc_info=True) + + # Validate copy was done + invalid_copy = [] + for src, dst in to_copy: + if not os.path.exists(dst) or not filecmp.cmp(src, dst): + invalid_copy.append((src, dst)) + + # Validate delete was dones + invalid_remove = [] + for filepath in to_remove: + if os.path.exists(filepath): + invalid_remove.append(filepath) + + if not invalid_remove and not invalid_copy: + return + + msg_parts = [] + if invalid_remove: + msg_parts.append( + "Failed to remove files: {}".format(", ".join(invalid_remove)) + ) + + if invalid_copy: + _invalid = [ + "\"{}\" -> \"{}\"".format(src, dst) + for src, dst in invalid_copy + ] + msg_parts.append( + "Failed to copy files: {}".format(", ".join(_invalid)) + ) + raise RuntimeError(" & ".join(msg_parts)) + + def _launch_tv_paint(self, launch_args): + flags = ( + subprocess.DETACHED_PROCESS + | subprocess.CREATE_NEW_PROCESS_GROUP + ) + env = os.environ.copy() + # Remove QuickTime from PATH on windows + # - quicktime overrides TVPaint's ffmpeg encode/decode which may + # cause issues on loading + if platform.system().lower() == "windows": + new_path = [] + for path in env["PATH"].split(os.pathsep): + if path and "quicktime" not in path.lower(): + new_path.append(path) + env["PATH"] = os.pathsep.join(new_path) + + kwargs = { + "env": env, + "creationflags": flags + } + self.process = subprocess.Popen(launch_args, **kwargs) + + def _create_routes(self): + self.websocket_rpc = BaseTVPaintRpc( + self, loop=self.websocket_server.loop + ) + self.websocket_server.add_route( + "*", "/", self.websocket_rpc.handle_request + ) + + def _start_webserver(self): + self.websocket_server.start() + # Make sure RPC is using same loop as websocket server + while not self.websocket_server.server_is_running: + time.sleep(0.1) + + def _stop_webserver(self): + self.websocket_server.stop() + + def _exit(self, exit_code=None): + self._stop_webserver() + if exit_code is not None: + self.exit_code = exit_code + + def stop(self): + """Stop communication and currently running python process.""" + log.info("Stopping communication") + self._exit() + + def launch(self, launch_args): + """Prepare all required data and launch host. + + First is prepared websocket server as communication point for host, + when server is ready to use host is launched as subprocess. + """ + if platform.system().lower() == "windows": + self._prepare_windows_plugin(launch_args) + + # Launch TVPaint and the websocket server. + log.info("Launching TVPaint") + self.websocket_server = WebSocketServer() + + self._create_routes() + + os.environ["WEBSOCKET_URL"] = "ws://localhost:{}".format( + self.websocket_server.port + ) + + log.info("Added request handler for url: {}".format( + os.environ["WEBSOCKET_URL"] + )) + + self._start_webserver() + + # Start TVPaint when server is running + self._launch_tv_paint(launch_args) + + log.info("Waiting for client connection") + while True: + if self.process.poll() is not None: + log.debug("Host process is not alive. Exiting") + self._exit(1) + return + + if self.websocket_rpc.client_connected(): + log.info("Client has connected") + break + time.sleep(0.5) + + self._on_client_connect() + + api.emit("application.launched") + + def _on_client_connect(self): + self._initial_textfile_write() + + def _initial_textfile_write(self): + """Show popup about Write to file at start of TVPaint.""" + tmp_file = tempfile.NamedTemporaryFile( + mode="w", prefix="a_tvp_", suffix=".txt", delete=False + ) + tmp_file.close() + tmp_filepath = tmp_file.name.replace("\\", "/") + george_script = ( + "tv_writetextfile \"strict\" \"append\" \"{}\" \"empty\"" + ).format(tmp_filepath) + + result = CommunicationWrapper.execute_george(george_script) + + # Remote the file + os.remove(tmp_filepath) + + if result is None: + log.warning( + "Host was probably closed before plugin was initialized." + ) + elif result.lower() == "forbidden": + log.warning("User didn't confirm saving files.") + + def _client(self): + if not self.websocket_rpc: + log.warning("Communicator's server did not start yet.") + return None + + for client in self.websocket_rpc.clients: + if not client.ws.closed: + return client + log.warning("Client is not yet connected to Communicator.") + return None + + def client(self): + if not self._connected_client or self._connected_client.ws.closed: + self._connected_client = self._client() + return self._connected_client + + def send_request(self, method, params=None): + client = self.client() + if not client: + return + + return self.websocket_rpc.send_request( + client, method, params + ) + + def send_notification(self, method, params=None): + client = self.client() + if not client: + return + + self.websocket_rpc.send_notification( + client, method, params + ) + + def execute_george(self, george_script): + """Execute passed goerge script in TVPaint.""" + return self.send_request( + "execute_george", [george_script] + ) + + def execute_george_through_file(self, george_script): + """Execute george script with temp file. + + Allows to execute multiline george script without stopping websocket + client. + + On windows make sure script does not contain paths with backwards + slashes in paths, TVPaint won't execute properly in that case. + + Args: + george_script (str): George script to execute. May be multilined. + """ + temporary_file = tempfile.NamedTemporaryFile( + mode="w", prefix="a_tvp_", suffix=".grg", delete=False + ) + temporary_file.write(george_script) + temporary_file.close() + temp_file_path = temporary_file.name.replace("\\", "/") + self.execute_george("tv_runscript {}".format(temp_file_path)) + os.remove(temp_file_path) + + +class QtCommunicator(BaseCommunicator): + menu_definitions = { + "title": "OpenPype Tools", + "menu_items": [ + { + "callback": "workfiles_tool", + "label": "Workfiles", + "help": "Open workfiles tool" + }, { + "callback": "loader_tool", + "label": "Load", + "help": "Open loader tool" + }, { + "callback": "creator_tool", + "label": "Create", + "help": "Open creator tool" + }, { + "callback": "scene_inventory_tool", + "label": "Scene inventory", + "help": "Open scene inventory tool" + }, { + "callback": "publish_tool", + "label": "Publish", + "help": "Open publisher" + }, { + "callback": "library_loader_tool", + "label": "Library", + "help": "Open library loader tool" + }, { + "callback": "subset_manager_tool", + "label": "Subset Manager", + "help": "Open subset manager tool" + }, { + "callback": "experimental_tools", + "label": "Experimental tools", + "help": "Open experimental tools dialog" + } + ] + } + + def __init__(self, qt_app): + super().__init__() + self.callback_queue = Queue() + self.qt_app = qt_app + + def _create_routes(self): + self.websocket_rpc = QtTVPaintRpc( + self, loop=self.websocket_server.loop + ) + self.websocket_server.add_route( + "*", "/", self.websocket_rpc.handle_request + ) + + def execute_in_main_thread(self, main_thread_item, wait=True): + """Add `MainThreadItem` to callback queue and wait for result.""" + self.callback_queue.put(main_thread_item) + if wait: + return main_thread_item.wait() + return + + async def async_execute_in_main_thread(self, main_thread_item, wait=True): + """Add `MainThreadItem` to callback queue and wait for result.""" + self.callback_queue.put(main_thread_item) + if wait: + return await main_thread_item.async_wait() + + def main_thread_listen(self): + """Get last `MainThreadItem` from queue. + + Must be called from main thread. + + Method checks if host process is still running as it may cause + issues if not. + """ + # check if host still running + if self.process.poll() is not None: + self._exit() + return None + + if self.callback_queue.empty(): + return None + return self.callback_queue.get() + + def _on_client_connect(self): + super()._on_client_connect() + self._build_menu() + + def _build_menu(self): + self.send_request( + "define_menu", [self.menu_definitions] + ) + + def _exit(self, *args, **kwargs): + super()._exit(*args, **kwargs) + api.emit("application.exit") + self.qt_app.exit(self.exit_code) From e36f0fe11ec240e033224c3c23605236e38c0fe0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 26 Nov 2021 15:50:26 +0100 Subject: [PATCH 019/492] copied and modified launch script --- openpype/hosts/tvpaint/api/launch_script.py | 84 +++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 openpype/hosts/tvpaint/api/launch_script.py diff --git a/openpype/hosts/tvpaint/api/launch_script.py b/openpype/hosts/tvpaint/api/launch_script.py new file mode 100644 index 00000000000..ec34442d28e --- /dev/null +++ b/openpype/hosts/tvpaint/api/launch_script.py @@ -0,0 +1,84 @@ +import os +import sys +import signal +import traceback +import ctypes +import platform +import logging + +from Qt import QtWidgets, QtCore, QtGui + +from avalon import api +from openpype import style +from openpype.hosts.tvpaint.api.communication_server import ( + CommunicationWrapper +) +from openpype.hosts.tvpaint import api as tvpaint_host + +log = logging.getLogger(__name__) + + +def safe_excepthook(*args): + traceback.print_exception(*args) + + +def main(launch_args): + # Be sure server won't crash at any moment but just print traceback + sys.excepthook = safe_excepthook + + # Create QtApplication for tools + # - QApplicaiton is also main thread/event loop of the server + qt_app = QtWidgets.QApplication([]) + + # Execute pipeline installation + api.install(tvpaint_host) + + # Create Communicator object and trigger launch + # - this must be done before anything is processed + communicator = CommunicationWrapper.create_communicator(qt_app) + communicator.launch(launch_args) + + def process_in_main_thread(): + """Execution of `MainThreadItem`.""" + item = communicator.main_thread_listen() + if item: + item.execute() + + timer = QtCore.QTimer() + timer.setInterval(100) + timer.timeout.connect(process_in_main_thread) + timer.start() + + # Register terminal signal handler + def signal_handler(*_args): + print("You pressed Ctrl+C. Process ended.") + communicator.stop() + + signal.signal(signal.SIGINT, signal_handler) + signal.signal(signal.SIGTERM, signal_handler) + + qt_app.setQuitOnLastWindowClosed(False) + qt_app.setStyleSheet(style.load_stylesheet()) + + # Load avalon icon + icon_path = style.app_icon_path() + if icon_path: + icon = QtGui.QIcon(icon_path) + qt_app.setWindowIcon(icon) + + # Set application name to be able show application icon in task bar + if platform.system().lower() == "windows": + ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID( + u"WebsocketServer" + ) + + # Run Qt application event processing + sys.exit(qt_app.exec_()) + + +if __name__ == "__main__": + args = list(sys.argv) + if os.path.abspath(__file__) == os.path.normpath(args[0]): + # Pop path to script + args.pop(0) + main(args) From 21d57f9e74f9244206c8314d99ab82014a185cb2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 26 Nov 2021 15:50:57 +0100 Subject: [PATCH 020/492] copied pipeline.py and added code which was in openpype init --- openpype/hosts/tvpaint/api/pipeline.py | 488 +++++++++++++++++++++++++ 1 file changed, 488 insertions(+) create mode 100644 openpype/hosts/tvpaint/api/pipeline.py diff --git a/openpype/hosts/tvpaint/api/pipeline.py b/openpype/hosts/tvpaint/api/pipeline.py new file mode 100644 index 00000000000..235d5525f49 --- /dev/null +++ b/openpype/hosts/tvpaint/api/pipeline.py @@ -0,0 +1,488 @@ +import os +import json +import contextlib +import tempfile +import logging + +import requests + +import pyblish.api +import avalon.api + +from avalon import io +from avalon.pipeline import AVALON_CONTAINER_ID + +from openpype.hosts import tvpaint +from openpype.api import get_current_project_settings + +from .communication_server import CommunicationWrapper + +log = logging.getLogger(__name__) + +HOST_DIR = os.path.dirname(os.path.abspath(tvpaint.__file__)) +PLUGINS_DIR = os.path.join(HOST_DIR, "plugins") +PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish") +LOAD_PATH = os.path.join(PLUGINS_DIR, "load") +CREATE_PATH = os.path.join(PLUGINS_DIR, "create") + +METADATA_SECTION = "avalon" +SECTION_NAME_CONTEXT = "context" +SECTION_NAME_INSTANCES = "instances" +SECTION_NAME_CONTAINERS = "containers" +# Maximum length of metadata chunk string +# TODO find out the max (500 is safe enough) +TVPAINT_CHUNK_LENGTH = 500 + +"""TVPaint's Metadata + +Metadata are stored to TVPaint's workfile. + +Workfile works similar to .ini file but has few limitation. Most important +limitation is that value under key has limited length. Due to this limitation +each metadata section/key stores number of "subkeys" that are related to +the section. + +Example: +Metadata key `"instances"` may have stored value "2". In that case it is +expected that there are also keys `["instances0", "instances1"]`. + +Workfile data looks like: +``` +[avalon] +instances0=[{{__dq__}id{__dq__}: {__dq__}pyblish.avalon.instance{__dq__... +instances1=...more data... +instances=2 +``` +""" + + +def install(): + """Install Maya-specific functionality of avalon-core. + + This function is called automatically on calling `api.install(maya)`. + + """ + log.info("OpenPype - Installing TVPaint integration") + io.install() + + # Create workdir folder if does not exist yet + workdir = io.Session["AVALON_WORKDIR"] + if not os.path.exists(workdir): + os.makedirs(workdir) + + pyblish.api.register_host("tvpaint") + pyblish.api.register_plugin_path(PUBLISH_PATH) + avalon.api.register_plugin_path(avalon.api.Loader, LOAD_PATH) + avalon.api.register_plugin_path(avalon.api.Creator, CREATE_PATH) + + registered_callbacks = ( + pyblish.api.registered_callbacks().get("instanceToggled") or [] + ) + if on_instance_toggle not in registered_callbacks: + pyblish.api.register_callback("instanceToggled", on_instance_toggle) + + avalon.api.on("application.launched", initial_launch) + avalon.api.on("application.exit", application_exit) + + +def uninstall(): + """Uninstall TVPaint-specific functionality of avalon-core. + + This function is called automatically on calling `api.uninstall()`. + + """ + log.info("OpenPype - Uninstalling TVPaint integration") + pyblish.api.deregister_host("tvpaint") + pyblish.api.deregister_plugin_path(PUBLISH_PATH) + avalon.api.deregister_plugin_path(avalon.api.Loader, LOAD_PATH) + avalon.api.deregister_plugin_path(avalon.api.Creator, CREATE_PATH) + + +def containerise( + name, namespace, members, context, loader, current_containers=None +): + """Add new container to metadata. + + Args: + name (str): Container name. + namespace (str): Container namespace. + members (list): List of members that were loaded and belongs + to the container (layer names). + current_containers (list): Preloaded containers. Should be used only + on update/switch when containers were modified durring the process. + + Returns: + dict: Container data stored to workfile metadata. + """ + + container_data = { + "schema": "openpype:container-2.0", + "id": AVALON_CONTAINER_ID, + "members": members, + "name": name, + "namespace": namespace, + "loader": str(loader), + "representation": str(context["representation"]["_id"]) + } + if current_containers is None: + current_containers = ls() + + # Add container to containers list + current_containers.append(container_data) + + # Store data to metadata + write_workfile_metadata(SECTION_NAME_CONTAINERS, current_containers) + + return container_data + + +@contextlib.contextmanager +def maintained_selection(): + # TODO implement logic + try: + yield + finally: + pass + + +def split_metadata_string(text, chunk_length=None): + """Split string by length. + + Split text to chunks by entered length. + Example: + ```python + text = "ABCDEFGHIJKLM" + result = split_metadata_string(text, 3) + print(result) + >>> ['ABC', 'DEF', 'GHI', 'JKL'] + ``` + + Args: + text (str): Text that will be split into chunks. + chunk_length (int): Single chunk size. Default chunk_length is + set to global variable `TVPAINT_CHUNK_LENGTH`. + + Returns: + list: List of strings wil at least one item. + """ + if chunk_length is None: + chunk_length = TVPAINT_CHUNK_LENGTH + chunks = [] + for idx in range(chunk_length, len(text) + chunk_length, chunk_length): + start_idx = idx - chunk_length + chunks.append(text[start_idx:idx]) + return chunks + + +def get_workfile_metadata_string_for_keys(metadata_keys): + """Read metadata for specific keys from current project workfile. + + All values from entered keys are stored to single string without separator. + + Function is designed to help get all values for one metadata key at once. + So order of passed keys matteres. + + Args: + metadata_keys (list, str): Metadata keys for which data should be + retrieved. Order of keys matters! It is possible to enter only + single key as string. + """ + # Add ability to pass only single key + if isinstance(metadata_keys, str): + metadata_keys = [metadata_keys] + + output_file = tempfile.NamedTemporaryFile( + mode="w", prefix="a_tvp_", suffix=".txt", delete=False + ) + output_file.close() + output_filepath = output_file.name.replace("\\", "/") + + george_script_parts = [] + george_script_parts.append( + "output_path = \"{}\"".format(output_filepath) + ) + # Store data for each index of metadata key + for metadata_key in metadata_keys: + george_script_parts.append( + "tv_readprojectstring \"{}\" \"{}\" \"\"".format( + METADATA_SECTION, metadata_key + ) + ) + george_script_parts.append( + "tv_writetextfile \"strict\" \"append\" '\"'output_path'\"' result" + ) + + # Execute the script + george_script = "\n".join(george_script_parts) + CommunicationWrapper.execute_george_through_file(george_script) + + # Load data from temp file + with open(output_filepath, "r") as stream: + file_content = stream.read() + + # Remove `\n` from content + output_string = file_content.replace("\n", "") + + # Delete temp file + os.remove(output_filepath) + + return output_string + + +def get_workfile_metadata_string(metadata_key): + """Read metadata for specific key from current project workfile.""" + result = get_workfile_metadata_string_for_keys([metadata_key]) + if not result: + return None + + stripped_result = result.strip() + if not stripped_result: + return None + + # NOTE Backwards compatibility when metadata key did not store range of key + # indexes but the value itself + # NOTE We don't have to care about negative values with `isdecimal` check + if not stripped_result.isdecimal(): + metadata_string = result + else: + keys = [] + for idx in range(int(stripped_result)): + keys.append("{}{}".format(metadata_key, idx)) + metadata_string = get_workfile_metadata_string_for_keys(keys) + + # Replace quotes plaholders with their values + metadata_string = ( + metadata_string + .replace("{__sq__}", "'") + .replace("{__dq__}", "\"") + ) + return metadata_string + + +def get_workfile_metadata(metadata_key, default=None): + """Read and parse metadata for specific key from current project workfile. + + Pipeline use function to store loaded and created instances within keys + stored in `SECTION_NAME_INSTANCES` and `SECTION_NAME_CONTAINERS` + constants. + + Args: + metadata_key (str): Key defying which key should read. It is expected + value contain json serializable string. + """ + if default is None: + default = [] + + json_string = get_workfile_metadata_string(metadata_key) + if json_string: + try: + return json.loads(json_string) + except json.decoder.JSONDecodeError: + # TODO remove when backwards compatibility of storing metadata + # will be removed + print(( + "Fixed invalid metadata in workfile." + " Not serializable string was: {}" + ).format(json_string)) + write_workfile_metadata(metadata_key, default) + return default + + +def write_workfile_metadata(metadata_key, value): + """Write metadata for specific key into current project workfile. + + George script has specific way how to work with quotes which should be + solved automatically with this function. + + Args: + metadata_key (str): Key defying under which key value will be stored. + value (dict,list,str): Data to store they must be json serializable. + """ + if isinstance(value, (dict, list)): + value = json.dumps(value) + + if not value: + value = "" + + # Handle quotes in dumped json string + # - replace single and double quotes with placeholders + value = ( + value + .replace("'", "{__sq__}") + .replace("\"", "{__dq__}") + ) + chunks = split_metadata_string(value) + chunks_len = len(chunks) + + write_template = "tv_writeprojectstring \"{}\" \"{}\" \"{}\"" + george_script_parts = [] + # Add information about chunks length to metadata key itself + george_script_parts.append( + write_template.format(METADATA_SECTION, metadata_key, chunks_len) + ) + # Add chunk values to indexed metadata keys + for idx, chunk_value in enumerate(chunks): + sub_key = "{}{}".format(metadata_key, idx) + george_script_parts.append( + write_template.format(METADATA_SECTION, sub_key, chunk_value) + ) + + george_script = "\n".join(george_script_parts) + + return CommunicationWrapper.execute_george_through_file(george_script) + + +def get_current_workfile_context(): + """Return context in which was workfile saved.""" + return get_workfile_metadata(SECTION_NAME_CONTEXT, {}) + + +def save_current_workfile_context(context): + """Save context which was used to create a workfile.""" + return write_workfile_metadata(SECTION_NAME_CONTEXT, context) + + +def remove_instance(instance): + """Remove instance from current workfile metadata.""" + current_instances = get_workfile_metadata(SECTION_NAME_INSTANCES) + instance_id = instance.get("uuid") + found_idx = None + if instance_id: + for idx, _inst in enumerate(current_instances): + if _inst["uuid"] == instance_id: + found_idx = idx + break + + if found_idx is None: + return + current_instances.pop(found_idx) + write_instances(current_instances) + + +def list_instances(): + """List all created instances from current workfile.""" + return get_workfile_metadata(SECTION_NAME_INSTANCES) + + +def write_instances(data): + return write_workfile_metadata(SECTION_NAME_INSTANCES, data) + + +# Backwards compatibility +def _write_instances(*args, **kwargs): + return write_instances(*args, **kwargs) + + +def ls(): + return get_workfile_metadata(SECTION_NAME_CONTAINERS) + + +def on_instance_toggle(instance, old_value, new_value): + """Update instance data in workfile on publish toggle.""" + # Review may not have real instance in wokrfile metadata + if not instance.data.get("uuid"): + return + + instance_id = instance.data["uuid"] + found_idx = None + current_instances = list_instances() + for idx, workfile_instance in enumerate(current_instances): + if workfile_instance["uuid"] == instance_id: + found_idx = idx + break + + if found_idx is None: + return + + if "active" in current_instances[found_idx]: + current_instances[found_idx]["active"] = new_value + write_instances(current_instances) + + +def initial_launch(): + # Setup project settings if its the template that's launched. + # TODO also check for template creation when it's possible to define + # templates + last_workfile = os.environ.get("AVALON_LAST_WORKFILE") + if not last_workfile or os.path.exists(last_workfile): + return + + log.info("Setting up project...") + set_context_settings() + + +def application_exit(): + data = get_current_project_settings() + stop_timer = data["tvpaint"]["stop_timer_on_application_exit"] + + if not stop_timer: + return + + # Stop application timer. + webserver_url = os.environ.get("OPENPYPE_WEBSERVER_URL") + rest_api_url = "{}/timers_manager/stop_timer".format(webserver_url) + requests.post(rest_api_url) + + +def set_context_settings(asset_doc=None): + """Set workfile settings by asset document data. + + Change fps, resolution and frame start/end. + """ + if asset_doc is None: + # Use current session asset if not passed + asset_doc = avalon.io.find_one({ + "type": "asset", + "name": avalon.io.Session["AVALON_ASSET"] + }) + + project_doc = avalon.io.find_one({"type": "project"}) + + framerate = asset_doc["data"].get("fps") + if framerate is None: + framerate = project_doc["data"].get("fps") + + if framerate is not None: + CommunicationWrapper.execute_george( + "tv_framerate {} \"timestretch\"".format(framerate) + ) + else: + print("Framerate was not found!") + + width_key = "resolutionWidth" + height_key = "resolutionHeight" + + width = asset_doc["data"].get(width_key) + height = asset_doc["data"].get(height_key) + if width is None or height is None: + width = project_doc["data"].get(width_key) + height = project_doc["data"].get(height_key) + + if width is None or height is None: + print("Resolution was not found!") + else: + CommunicationWrapper.execute_george( + "tv_resizepage {} {} 0".format(width, height) + ) + + frame_start = asset_doc["data"].get("frameStart") + frame_end = asset_doc["data"].get("frameEnd") + + if frame_start is None or frame_end is None: + print("Frame range was not found!") + return + + handles = asset_doc["data"].get("handles") or 0 + handle_start = asset_doc["data"].get("handleStart") + handle_end = asset_doc["data"].get("handleEnd") + + if handle_start is None or handle_end is None: + handle_start = handles + handle_end = handles + + # Always start from 0 Mark In and set only Mark Out + mark_in = 0 + mark_out = mark_in + (frame_end - frame_start) + handle_start + handle_end + + CommunicationWrapper.execute_george("tv_markin {} set".format(mark_in)) + CommunicationWrapper.execute_george("tv_markout {} set".format(mark_out)) From 7f34b6ee432315c7d8032b344cd33a174904d1ff Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 26 Nov 2021 15:52:05 +0100 Subject: [PATCH 021/492] copied workio --- openpype/hosts/tvpaint/api/workio.py | 55 ++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 openpype/hosts/tvpaint/api/workio.py diff --git a/openpype/hosts/tvpaint/api/workio.py b/openpype/hosts/tvpaint/api/workio.py new file mode 100644 index 00000000000..c513bec6cf5 --- /dev/null +++ b/openpype/hosts/tvpaint/api/workio.py @@ -0,0 +1,55 @@ +"""Host API required for Work Files. +# TODO @iLLiCiT implement functions: + has_unsaved_changes +""" + +from avalon import api +from .lib import ( + execute_george, + execute_george_through_file +) +from .pipeline import save_current_workfile_context + + +def open_file(filepath): + """Open the scene file in Blender.""" + george_script = "tv_LoadProject '\"'\"{}\"'\"'".format( + filepath.replace("\\", "/") + ) + return execute_george_through_file(george_script) + + +def save_file(filepath): + """Save the open scene file.""" + # Store context to workfile before save + context = { + "project": api.Session["AVALON_PROJECT"], + "asset": api.Session["AVALON_ASSET"], + "task": api.Session["AVALON_TASK"] + } + save_current_workfile_context(context) + + # Execute george script to save workfile. + george_script = "tv_SaveProject {}".format(filepath.replace("\\", "/")) + return execute_george(george_script) + + +def current_file(): + """Return the path of the open scene file.""" + george_script = "tv_GetProjectName" + return execute_george(george_script) + + +def has_unsaved_changes(): + """Does the open scene file have unsaved changes?""" + return False + + +def file_extensions(): + """Return the supported file extensions for Blender scene files.""" + return api.HOST_WORKFILE_EXTENSIONS["tvpaint"] + + +def work_root(session): + """Return the default root to browse for work files.""" + return session["AVALON_WORKDIR"] From b745f86cf32bb6e70717d7423270731d359fc431 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 26 Nov 2021 16:03:02 +0100 Subject: [PATCH 022/492] copied lib functions --- openpype/hosts/tvpaint/api/lib.py | 572 +++++++++++++++++++++++++++--- 1 file changed, 522 insertions(+), 50 deletions(-) diff --git a/openpype/hosts/tvpaint/api/lib.py b/openpype/hosts/tvpaint/api/lib.py index 8e63c9e5b25..e41bbb5aadf 100644 --- a/openpype/hosts/tvpaint/api/lib.py +++ b/openpype/hosts/tvpaint/api/lib.py @@ -1,66 +1,538 @@ -from PIL import Image +import os +import logging +import tempfile import avalon.io -from avalon.tvpaint.lib import execute_george +from . import CommunicationWrapper +from .pipeline import ( + list_instances, + write_instances +) -def set_context_settings(asset_doc=None): - """Set workfile settings by asset document data. +log = logging.getLogger(__name__) - Change fps, resolution and frame start/end. + +def execute_george(george_script, communicator=None): + if not communicator: + communicator = CommunicationWrapper.communicator + return communicator.execute_george(george_script) + + +def execute_george_through_file(george_script, communicator=None): + """Execute george script with temp file. + + Allows to execute multiline george script without stopping websocket + client. + + On windows make sure script does not contain paths with backwards + slashes in paths, TVPaint won't execute properly in that case. + + Args: + george_script (str): George script to execute. May be multilined. """ - if asset_doc is None: - # Use current session asset if not passed - asset_doc = avalon.io.find_one({ - "type": "asset", - "name": avalon.io.Session["AVALON_ASSET"] - }) - - project_doc = avalon.io.find_one({"type": "project"}) - - framerate = asset_doc["data"].get("fps") - if framerate is None: - framerate = project_doc["data"].get("fps") - - if framerate is not None: - execute_george( - "tv_framerate {} \"timestretch\"".format(framerate) - ) - else: - print("Framerate was not found!") + if not communicator: + communicator = CommunicationWrapper.communicator + + return communicator.execute_george_through_file(george_script) + + +def parse_layers_data(data): + """Parse layers data loaded in 'get_layers_data'.""" + layers = [] + layers_raw = data.split("\n") + for layer_raw in layers_raw: + layer_raw = layer_raw.strip() + if not layer_raw: + continue + ( + layer_id, group_id, visible, position, opacity, name, + layer_type, + frame_start, frame_end, prelighttable, postlighttable, + selected, editable, sencil_state + ) = layer_raw.split("|") + layer = { + "layer_id": int(layer_id), + "group_id": int(group_id), + "visible": visible == "ON", + "position": int(position), + "opacity": int(opacity), + "name": name, + "type": layer_type, + "frame_start": int(frame_start), + "frame_end": int(frame_end), + "prelighttable": prelighttable == "1", + "postlighttable": postlighttable == "1", + "selected": selected == "1", + "editable": editable == "1", + "sencil_state": sencil_state + } + layers.append(layer) + return layers - width_key = "resolutionWidth" - height_key = "resolutionHeight" - width = asset_doc["data"].get(width_key) - height = asset_doc["data"].get(height_key) - if width is None or height is None: - width = project_doc["data"].get(width_key) - height = project_doc["data"].get(height_key) +def get_layers_data_george_script(output_filepath, layer_ids=None): + """Prepare george script which will collect all layers from workfile.""" + output_filepath = output_filepath.replace("\\", "/") + george_script_lines = [ + # Variable containing full path to output file + "output_path = \"{}\"".format(output_filepath), + # Get Current Layer ID + "tv_LayerCurrentID", + "current_layer_id = result" + ] + # Script part for getting and storing layer information to temp + layer_data_getter = ( + # Get information about layer's group + "tv_layercolor \"get\" layer_id", + "group_id = result", + "tv_LayerInfo layer_id", + ( + "PARSE result visible position opacity name" + " type startFrame endFrame prelighttable postlighttable" + " selected editable sencilState" + ), + # Check if layer ID match `tv_LayerCurrentID` + "IF CMP(current_layer_id, layer_id)==1", + # - mark layer as selected if layer id match to current layer id + "selected=1", + "END", + # Prepare line with data separated by "|" + ( + "line = layer_id'|'group_id'|'visible'|'position'|'opacity'|'" + "name'|'type'|'startFrame'|'endFrame'|'prelighttable'|'" + "postlighttable'|'selected'|'editable'|'sencilState" + ), + # Write data to output file + "tv_writetextfile \"strict\" \"append\" '\"'output_path'\"' line", + ) - if width is None or height is None: - print("Resolution was not found!") + # Collect data for all layers if layers are not specified + if layer_ids is None: + george_script_lines.extend(( + # Layer loop variables + "loop = 1", + "idx = 0", + # Layers loop + "WHILE loop", + "tv_LayerGetID idx", + "layer_id = result", + "idx = idx + 1", + # Stop loop if layer_id is "NONE" + "IF CMP(layer_id, \"NONE\")==1", + "loop = 0", + "ELSE", + *layer_data_getter, + "END", + "END" + )) else: - execute_george("tv_resizepage {} {} 0".format(width, height)) + for layer_id in layer_ids: + george_script_lines.append("layer_id = {}".format(layer_id)) + george_script_lines.extend(layer_data_getter) + + return "\n".join(george_script_lines) + + +def layers_data(layer_ids=None, communicator=None): + """Backwards compatible function of 'get_layers_data'.""" + return get_layers_data(layer_ids, communicator) + + +def get_layers_data(layer_ids=None, communicator=None): + """Collect all layers information from currently opened workfile.""" + output_file = tempfile.NamedTemporaryFile( + mode="w", prefix="a_tvp_", suffix=".txt", delete=False + ) + output_file.close() + if layer_ids is not None and isinstance(layer_ids, int): + layer_ids = [layer_ids] + + output_filepath = output_file.name + + george_script = get_layers_data_george_script(output_filepath, layer_ids) + + execute_george_through_file(george_script, communicator) + + with open(output_filepath, "r") as stream: + data = stream.read() + + output = parse_layers_data(data) + os.remove(output_filepath) + return output + + +def parse_group_data(data): + """Paser group data collected in 'get_groups_data'.""" + output = [] + groups_raw = data.split("\n") + for group_raw in groups_raw: + group_raw = group_raw.strip() + if not group_raw: + continue + + parts = group_raw.split(" ") + # Check for length and concatenate 2 last items until length match + # - this happens if name contain spaces + while len(parts) > 6: + last_item = parts.pop(-1) + parts[-1] = " ".join([parts[-1], last_item]) + clip_id, group_id, red, green, blue, name = parts + + group = { + "group_id": int(group_id), + "name": name, + "clip_id": int(clip_id), + "red": int(red), + "green": int(green), + "blue": int(blue), + } + output.append(group) + return output + + +def groups_data(communicator=None): + """Backwards compatible function of 'get_groups_data'.""" + return get_groups_data(communicator) + + +def get_groups_data(communicator=None): + """Information about groups from current workfile.""" + output_file = tempfile.NamedTemporaryFile( + mode="w", prefix="a_tvp_", suffix=".txt", delete=False + ) + output_file.close() + + output_filepath = output_file.name.replace("\\", "/") + george_script_lines = ( + # Variable containing full path to output file + "output_path = \"{}\"".format(output_filepath), + "loop = 1", + "FOR idx = 1 TO 12", + "tv_layercolor \"getcolor\" 0 idx", + "tv_writetextfile \"strict\" \"append\" '\"'output_path'\"' result", + "END" + ) + george_script = "\n".join(george_script_lines) + execute_george_through_file(george_script, communicator) + + with open(output_filepath, "r") as stream: + data = stream.read() + + output = parse_group_data(data) + os.remove(output_filepath) + return output + + +def get_layers_pre_post_behavior(layer_ids, communicator=None): + """Collect data about pre and post behavior of layer ids. + + Pre and Post behaviors is enumerator of possible values: + - "none" + - "repeat" / "loop" + - "pingpong" + - "hold" + + Example output: + ```json + { + 0: { + "pre": "none", + "post": "loop" + } + } + ``` + + Returns: + dict: Key is layer id value is dictionary with "pre" and "post" keys. + """ + # Skip if is empty + if not layer_ids: + return {} + + # Auto convert to list + if not isinstance(layer_ids, (list, set, tuple)): + layer_ids = [layer_ids] + + # Prepare temp file + output_file = tempfile.NamedTemporaryFile( + mode="w", prefix="a_tvp_", suffix=".txt", delete=False + ) + output_file.close() + + output_filepath = output_file.name.replace("\\", "/") + george_script_lines = [ + # Variable containing full path to output file + "output_path = \"{}\"".format(output_filepath), + ] + for layer_id in layer_ids: + george_script_lines.extend([ + "layer_id = {}".format(layer_id), + "tv_layerprebehavior layer_id", + "pre_beh = result", + "tv_layerpostbehavior layer_id", + "post_beh = result", + "line = layer_id'|'pre_beh'|'post_beh", + "tv_writetextfile \"strict\" \"append\" '\"'output_path'\"' line" + ]) - frame_start = asset_doc["data"].get("frameStart") - frame_end = asset_doc["data"].get("frameEnd") + george_script = "\n".join(george_script_lines) + execute_george_through_file(george_script, communicator) - if frame_start is None or frame_end is None: - print("Frame range was not found!") - return + # Read data + with open(output_filepath, "r") as stream: + data = stream.read() - handles = asset_doc["data"].get("handles") or 0 - handle_start = asset_doc["data"].get("handleStart") - handle_end = asset_doc["data"].get("handleEnd") + # Remove temp file + os.remove(output_filepath) + + # Parse data + output = {} + raw_lines = data.split("\n") + for raw_line in raw_lines: + line = raw_line.strip() + if not line: + continue + parts = line.split("|") + if len(parts) != 3: + continue + layer_id, pre_beh, post_beh = parts + output[int(layer_id)] = { + "pre": pre_beh.lower(), + "post": post_beh.lower() + } + return output + + +def get_layers_exposure_frames(layer_ids, layers_data=None, communicator=None): + """Get exposure frames. + + Easily said returns frames where keyframes are. Recognized with george + function `tv_exposureinfo` returning "Head". + + Args: + layer_ids (list): Ids of a layers for which exposure frames should + look for. + layers_data (list): Precollected layers data. If are not passed then + 'get_layers_data' is used. + communicator (BaseCommunicator): Communicator used for communication + with TVPaint. + + Returns: + dict: Frames where exposure is set to "Head" by layer id. + """ + + if layers_data is None: + layers_data = get_layers_data(layer_ids) + _layers_by_id = { + layer["layer_id"]: layer + for layer in layers_data + } + layers_by_id = { + layer_id: _layers_by_id.get(layer_id) + for layer_id in layer_ids + } + tmp_file = tempfile.NamedTemporaryFile( + mode="w", prefix="a_tvp_", suffix=".txt", delete=False + ) + tmp_file.close() + tmp_output_path = tmp_file.name.replace("\\", "/") + george_script_lines = [ + "output_path = \"{}\"".format(tmp_output_path) + ] + + output = {} + layer_id_mapping = {} + for layer_id, layer_data in layers_by_id.items(): + layer_id_mapping[str(layer_id)] = layer_id + output[layer_id] = [] + if not layer_data: + continue + first_frame = layer_data["frame_start"] + last_frame = layer_data["frame_end"] + george_script_lines.extend([ + "line = \"\"", + "layer_id = {}".format(layer_id), + "line = line''layer_id", + "tv_layerset layer_id", + "frame = {}".format(first_frame), + "WHILE (frame <= {})".format(last_frame), + "tv_exposureinfo frame", + "exposure = result", + "IF (CMP(exposure, \"Head\") == 1)", + "line = line'|'frame", + "END", + "frame = frame + 1", + "END", + "tv_writetextfile \"strict\" \"append\" '\"'output_path'\"' line" + ]) + + execute_george_through_file("\n".join(george_script_lines), communicator) + + with open(tmp_output_path, "r") as stream: + data = stream.read() + + os.remove(tmp_output_path) + + lines = [] + for line in data.split("\n"): + line = line.strip() + if line: + lines.append(line) + + for line in lines: + line_items = list(line.split("|")) + layer_id = line_items.pop(0) + _layer_id = layer_id_mapping[layer_id] + output[_layer_id] = [int(frame) for frame in line_items] + + return output + + +def get_exposure_frames( + layer_id, first_frame=None, last_frame=None, communicator=None +): + """Get exposure frames. + + Easily said returns frames where keyframes are. Recognized with george + function `tv_exposureinfo` returning "Head". + + Args: + layer_id (int): Id of a layer for which exposure frames should + look for. + first_frame (int): From which frame will look for exposure frames. + Used layers first frame if not entered. + last_frame (int): Last frame where will look for exposure frames. + Used layers last frame if not entered. + + Returns: + list: Frames where exposure is set to "Head". + """ + if first_frame is None or last_frame is None: + layer = layers_data(layer_id)[0] + if first_frame is None: + first_frame = layer["frame_start"] + if last_frame is None: + last_frame = layer["frame_end"] + + tmp_file = tempfile.NamedTemporaryFile( + mode="w", prefix="a_tvp_", suffix=".txt", delete=False + ) + tmp_file.close() + tmp_output_path = tmp_file.name.replace("\\", "/") + george_script_lines = [ + "tv_layerset {}".format(layer_id), + "output_path = \"{}\"".format(tmp_output_path), + "output = \"\"", + "frame = {}".format(first_frame), + "WHILE (frame <= {})".format(last_frame), + "tv_exposureinfo frame", + "exposure = result", + "IF (CMP(exposure, \"Head\") == 1)", + "IF (CMP(output, \"\") == 1)", + "output = output''frame", + "ELSE", + "output = output'|'frame", + "END", + "END", + "frame = frame + 1", + "END", + "tv_writetextfile \"strict\" \"append\" '\"'output_path'\"' output" + ] + + execute_george_through_file("\n".join(george_script_lines), communicator) + + with open(tmp_output_path, "r") as stream: + data = stream.read() + + os.remove(tmp_output_path) + + lines = [] + for line in data.split("\n"): + line = line.strip() + if line: + lines.append(line) + + exposure_frames = [] + for line in lines: + for frame in line.split("|"): + exposure_frames.append(int(frame)) + return exposure_frames + + +def get_scene_data(communicator=None): + """Scene data of currently opened scene. + + Result contains resolution, pixel aspect, fps mark in/out with states, + frame start and background color. + + Returns: + dict: Scene data collected in many ways. + """ + workfile_info = execute_george("tv_projectinfo", communicator) + workfile_info_parts = workfile_info.split(" ") + + # Project frame start - not used + workfile_info_parts.pop(-1) + field_order = workfile_info_parts.pop(-1) + frame_rate = float(workfile_info_parts.pop(-1)) + pixel_apsect = float(workfile_info_parts.pop(-1)) + height = int(workfile_info_parts.pop(-1)) + width = int(workfile_info_parts.pop(-1)) + + # Marks return as "{frame - 1} {state} ", example "0 set". + result = execute_george("tv_markin", communicator) + mark_in_frame, mark_in_state, _ = result.split(" ") + + result = execute_george("tv_markout", communicator) + mark_out_frame, mark_out_state, _ = result.split(" ") + + start_frame = execute_george("tv_startframe", communicator) + return { + "width": width, + "height": height, + "pixel_aspect": pixel_apsect, + "fps": frame_rate, + "field_order": field_order, + "mark_in": int(mark_in_frame), + "mark_in_state": mark_in_state, + "mark_in_set": mark_in_state == "set", + "mark_out": int(mark_out_frame), + "mark_out_state": mark_out_state, + "mark_out_set": mark_out_state == "set", + "start_frame": int(start_frame), + "bg_color": get_scene_bg_color(communicator) + } + + +def get_scene_bg_color(communicator=None): + """Background color set on scene. + + Is important for review exporting where scene bg color is used as + background. + """ + output_file = tempfile.NamedTemporaryFile( + mode="w", prefix="a_tvp_", suffix=".txt", delete=False + ) + output_file.close() + output_filepath = output_file.name.replace("\\", "/") + george_script_lines = [ + # Variable containing full path to output file + "output_path = \"{}\"".format(output_filepath), + "tv_background", + "bg_color = result", + # Write data to output file + "tv_writetextfile \"strict\" \"append\" '\"'output_path'\"' bg_color" + ] - if handle_start is None or handle_end is None: - handle_start = handles - handle_end = handles + george_script = "\n".join(george_script_lines) + execute_george_through_file(george_script, communicator) - # Always start from 0 Mark In and set only Mark Out - mark_in = 0 - mark_out = mark_in + (frame_end - frame_start) + handle_start + handle_end + with open(output_filepath, "r") as stream: + data = stream.read() - execute_george("tv_markin {} set".format(mark_in)) - execute_george("tv_markout {} set".format(mark_out)) + os.remove(output_filepath) + data = data.strip() + if not data: + return None + return data.split(" ") From c748d6d3402b57dbc04ffb1d12b08f7a1c8be491 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 26 Nov 2021 16:03:49 +0100 Subject: [PATCH 023/492] completed plugin.py with Creator and Loader from avalon's pipeline --- openpype/hosts/tvpaint/api/plugin.py | 109 ++++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/tvpaint/api/plugin.py b/openpype/hosts/tvpaint/api/plugin.py index e148e44a270..e65c25b8d1e 100644 --- a/openpype/hosts/tvpaint/api/plugin.py +++ b/openpype/hosts/tvpaint/api/plugin.py @@ -1,8 +1,21 @@ +import re +import uuid + +import avalon.api + from openpype.api import PypeCreatorMixin -from avalon.tvpaint import pipeline +from openpype.hosts.tvpaint.api import ( + pipeline, + lib +) + +class Creator(PypeCreatorMixin, avalon.api.Creator): + def __init__(self, *args, **kwargs): + super(Creator, self).__init__(*args, **kwargs) + # Add unified identifier created with `uuid` module + self.data["uuid"] = str(uuid.uuid4()) -class Creator(PypeCreatorMixin, pipeline.Creator): @classmethod def get_dynamic_data(cls, *args, **kwargs): dynamic_data = super(Creator, cls).get_dynamic_data(*args, **kwargs) @@ -17,3 +30,95 @@ def get_dynamic_data(cls, *args, **kwargs): if "task" not in dynamic_data and task_name: dynamic_data["task"] = task_name return dynamic_data + + @staticmethod + def are_instances_same(instance_1, instance_2): + """Compare instances but skip keys with unique values. + + During compare are skiped keys that will be 100% sure + different on new instance, like "id". + + Returns: + bool: True if instances are same. + """ + if ( + not isinstance(instance_1, dict) + or not isinstance(instance_2, dict) + ): + return instance_1 == instance_2 + + checked_keys = set() + checked_keys.add("id") + for key, value in instance_1.items(): + if key not in checked_keys: + if key not in instance_2: + return False + if value != instance_2[key]: + return False + checked_keys.add(key) + + for key in instance_2.keys(): + if key not in checked_keys: + return False + return True + + def write_instances(self, data): + self.log.debug( + "Storing instance data to workfile. {}".format(str(data)) + ) + return pipeline.write_instances(data) + + def process(self): + data = pipeline.list_instances() + data.append(self.data) + self.write_instances(data) + + +class Loader(avalon.api.Loader): + hosts = ["tvpaint"] + + @staticmethod + def get_members_from_container(container): + if "members" not in container and "objectName" in container: + # Backwards compatibility + layer_ids_str = container.get("objectName") + return [ + int(layer_id) for layer_id in layer_ids_str.split("|") + ] + return container["members"] + + def get_unique_layer_name(self, asset_name, name): + """Layer name with counter as suffix. + + Find higher 3 digit suffix from all layer names in scene matching regex + `{asset_name}_{name}_{suffix}`. Higher 3 digit suffix is used + as base for next number if scene does not contain layer matching regex + `0` is used ase base. + + Args: + asset_name (str): Name of subset's parent asset document. + name (str): Name of loaded subset. + + Returns: + (str): `{asset_name}_{name}_{higher suffix + 1}` + """ + layer_name_base = "{}_{}".format(asset_name, name) + + counter_regex = re.compile(r"_(\d{3})$") + + higher_counter = 0 + for layer in lib.get_layers_data(): + layer_name = layer["name"] + if not layer_name.startswith(layer_name_base): + continue + number_subpart = layer_name[len(layer_name_base):] + groups = counter_regex.findall(number_subpart) + if len(groups) != 1: + continue + + counter = int(groups[0]) + if counter > higher_counter: + higher_counter = counter + continue + + return "{}_{:0>3d}".format(layer_name_base, higher_counter + 1) From e419aaabce684e0503b68806cf1b018160f353cc Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 26 Nov 2021 16:03:58 +0100 Subject: [PATCH 024/492] modified init --- openpype/hosts/tvpaint/api/__init__.py | 142 +++++++++---------------- 1 file changed, 49 insertions(+), 93 deletions(-) diff --git a/openpype/hosts/tvpaint/api/__init__.py b/openpype/hosts/tvpaint/api/__init__.py index 1c50987d6d5..c461b33f4b0 100644 --- a/openpype/hosts/tvpaint/api/__init__.py +++ b/openpype/hosts/tvpaint/api/__init__.py @@ -1,93 +1,49 @@ -import os -import logging - -import requests - -import avalon.api -import pyblish.api -from avalon.tvpaint import pipeline -from avalon.tvpaint.communication_server import register_localization_file -from .lib import set_context_settings - -from openpype.hosts import tvpaint -from openpype.api import get_current_project_settings - -log = logging.getLogger(__name__) - -HOST_DIR = os.path.dirname(os.path.abspath(tvpaint.__file__)) -PLUGINS_DIR = os.path.join(HOST_DIR, "plugins") -PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish") -LOAD_PATH = os.path.join(PLUGINS_DIR, "load") -CREATE_PATH = os.path.join(PLUGINS_DIR, "create") - - -def on_instance_toggle(instance, old_value, new_value): - # Review may not have real instance in wokrfile metadata - if not instance.data.get("uuid"): - return - - instance_id = instance.data["uuid"] - found_idx = None - current_instances = pipeline.list_instances() - for idx, workfile_instance in enumerate(current_instances): - if workfile_instance["uuid"] == instance_id: - found_idx = idx - break - - if found_idx is None: - return - - if "active" in current_instances[found_idx]: - current_instances[found_idx]["active"] = new_value - pipeline._write_instances(current_instances) - - -def initial_launch(): - # Setup project settings if its the template that's launched. - # TODO also check for template creation when it's possible to define - # templates - last_workfile = os.environ.get("AVALON_LAST_WORKFILE") - if not last_workfile or os.path.exists(last_workfile): - return - - log.info("Setting up project...") - set_context_settings() - - -def application_exit(): - data = get_current_project_settings() - stop_timer = data["tvpaint"]["stop_timer_on_application_exit"] - - if not stop_timer: - return - - # Stop application timer. - webserver_url = os.environ.get("OPENPYPE_WEBSERVER_URL") - rest_api_url = "{}/timers_manager/stop_timer".format(webserver_url) - requests.post(rest_api_url) - - -def install(): - log.info("OpenPype - Installing TVPaint integration") - localization_file = os.path.join(HOST_DIR, "resources", "avalon.loc") - register_localization_file(localization_file) - - pyblish.api.register_plugin_path(PUBLISH_PATH) - avalon.api.register_plugin_path(avalon.api.Loader, LOAD_PATH) - avalon.api.register_plugin_path(avalon.api.Creator, CREATE_PATH) - - registered_callbacks = ( - pyblish.api.registered_callbacks().get("instanceToggled") or [] - ) - if on_instance_toggle not in registered_callbacks: - pyblish.api.register_callback("instanceToggled", on_instance_toggle) - - avalon.api.on("application.launched", initial_launch) - avalon.api.on("application.exit", application_exit) - - -def uninstall(): - log.info("OpenPype - Uninstalling TVPaint integration") - pyblish.api.deregister_plugin_path(PUBLISH_PATH) - avalon.api.deregister_plugin_path(avalon.api.Loader, LOAD_PATH) - avalon.api.deregister_plugin_path(avalon.api.Creator, CREATE_PATH) +from .communication_server import CommunicationWrapper +from . import lib +from . import launch_script +from . import workio +from . import pipeline +from . import plugin +from .pipeline import ( + install, + uninstall, + maintained_selection, + remove_instance, + list_instances, + ls +) + +from .workio import ( + open_file, + save_file, + current_file, + has_unsaved_changes, + file_extensions, + work_root, +) + + +__all__ = ( + "CommunicationWrapper", + + "lib", + "launch_script", + "workio", + "pipeline", + "plugin", + + "install", + "uninstall", + "maintained_selection", + "remove_instance", + "list_instances", + "ls", + + # Workfiles API + "open_file", + "save_file", + "current_file", + "has_unsaved_changes", + "file_extensions", + "work_root" +) From 0098ea546665733e8d9ca11ecc42729cdc76eae6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 26 Nov 2021 16:06:04 +0100 Subject: [PATCH 025/492] modified plugins --- .../tvpaint/plugins/create/create_render_layer.py | 15 ++++++++------- .../tvpaint/plugins/create/create_render_pass.py | 6 +++--- openpype/hosts/tvpaint/plugins/load/load_image.py | 4 ++-- .../tvpaint/plugins/load/load_reference_image.py | 4 ++-- openpype/hosts/tvpaint/plugins/load/load_sound.py | 4 ++-- .../hosts/tvpaint/plugins/load/load_workfile.py | 6 +++--- .../plugins/publish/collect_workfile_data.py | 2 +- .../plugins/publish/increment_workfile_version.py | 2 +- .../plugins/publish/validate_asset_name.py | 2 +- .../tvpaint/plugins/publish/validate_marks.py | 2 +- .../plugins/publish/validate_start_frame.py | 2 +- .../plugins/publish/validate_workfile_metadata.py | 2 +- 12 files changed, 26 insertions(+), 25 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render_layer.py b/openpype/hosts/tvpaint/plugins/create/create_render_layer.py index af6c0f0eee6..c43c136f3e1 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render_layer.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render_layer.py @@ -1,11 +1,12 @@ from avalon.api import CreatorError -from avalon.tvpaint import ( + +from openpype.lib import prepare_template_data +from openpype.hosts.tvpaint.api import ( + plugin, pipeline, lib, CommunicationWrapper ) -from openpype.hosts.tvpaint.api import plugin -from openpype.lib import prepare_template_data class CreateRenderlayer(plugin.Creator): @@ -56,7 +57,7 @@ def get_default_variant(cls): # Validate that communication is initialized if CommunicationWrapper.communicator: # Get currently selected layers - layers_data = lib.layers_data() + layers_data = lib.get_layers_data() selected_layers = [ layer @@ -75,7 +76,7 @@ def get_default_variant(cls): def process(self): self.log.debug("Query data from workfile.") instances = pipeline.list_instances() - layers_data = lib.layers_data() + layers_data = lib.get_layers_data() self.log.debug("Checking for selection groups.") # Collect group ids from selection @@ -102,7 +103,7 @@ def process(self): self.log.debug(f"Selected group id is \"{group_id}\".") self.data["group_id"] = group_id - group_data = lib.groups_data() + group_data = lib.get_groups_data() group_name = None for group in group_data: if group["group_id"] == group_id: @@ -169,7 +170,7 @@ def process(self): return self.log.debug("Querying groups data from workfile.") - groups_data = lib.groups_data() + groups_data = lib.get_groups_data() self.log.debug("Changing name of the group.") selected_group = None diff --git a/openpype/hosts/tvpaint/plugins/create/create_render_pass.py b/openpype/hosts/tvpaint/plugins/create/create_render_pass.py index ad06520210d..af962052fc7 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render_pass.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render_pass.py @@ -1,11 +1,11 @@ from avalon.api import CreatorError -from avalon.tvpaint import ( +from openpype.lib import prepare_template_data +from openpype.hosts.tvpaint.api import ( + plugin, pipeline, lib, CommunicationWrapper ) -from openpype.hosts.tvpaint.api import plugin -from openpype.lib import prepare_template_data class CreateRenderPass(plugin.Creator): diff --git a/openpype/hosts/tvpaint/plugins/load/load_image.py b/openpype/hosts/tvpaint/plugins/load/load_image.py index f77fab87f8b..1246fe82481 100644 --- a/openpype/hosts/tvpaint/plugins/load/load_image.py +++ b/openpype/hosts/tvpaint/plugins/load/load_image.py @@ -1,8 +1,8 @@ from avalon.vendor import qargparse -from avalon.tvpaint import lib, pipeline +from openpype.hosts.tvpaint.api import lib, plugin -class ImportImage(pipeline.Loader): +class ImportImage(plugin.Loader): """Load image or image sequence to TVPaint as new layer.""" families = ["render", "image", "background", "plate"] diff --git a/openpype/hosts/tvpaint/plugins/load/load_reference_image.py b/openpype/hosts/tvpaint/plugins/load/load_reference_image.py index b8b20ed20aa..b5e0a866863 100644 --- a/openpype/hosts/tvpaint/plugins/load/load_reference_image.py +++ b/openpype/hosts/tvpaint/plugins/load/load_reference_image.py @@ -1,10 +1,10 @@ import collections from avalon.pipeline import get_representation_context from avalon.vendor import qargparse -from avalon.tvpaint import lib, pipeline +from openpype.hosts.tvpaint.api import lib, pipeline, plugin -class LoadImage(pipeline.Loader): +class LoadImage(plugin.Loader): """Load image or image sequence to TVPaint as new layer.""" families = ["render", "image", "background", "plate"] diff --git a/openpype/hosts/tvpaint/plugins/load/load_sound.py b/openpype/hosts/tvpaint/plugins/load/load_sound.py index c83748fe06c..3f42370f5c8 100644 --- a/openpype/hosts/tvpaint/plugins/load/load_sound.py +++ b/openpype/hosts/tvpaint/plugins/load/load_sound.py @@ -1,9 +1,9 @@ import os import tempfile -from avalon.tvpaint import lib, pipeline +from openpype.hosts.tvpaint.api import lib, plugin -class ImportSound(pipeline.Loader): +class ImportSound(plugin.Loader): """Load sound to TVPaint. Sound layers does not have ids but only position index so we can't diff --git a/openpype/hosts/tvpaint/plugins/load/load_workfile.py b/openpype/hosts/tvpaint/plugins/load/load_workfile.py index f410a1ab9db..33e2a76cc96 100644 --- a/openpype/hosts/tvpaint/plugins/load/load_workfile.py +++ b/openpype/hosts/tvpaint/plugins/load/load_workfile.py @@ -1,16 +1,16 @@ import getpass import os -from avalon.tvpaint import lib, pipeline, get_current_workfile_context from avalon import api, io from openpype.lib import ( get_workfile_template_key_from_context, get_workdir_data ) from openpype.api import Anatomy +from openpype.hosts.tvpaint.api import lib, pipeline, plugin -class LoadWorkfile(pipeline.Loader): +class LoadWorkfile(plugin.Loader): """Load workfile.""" families = ["workfile"] @@ -24,7 +24,7 @@ def load(self, context, name, namespace, options): host = api.registered_host() current_file = host.current_file() - context = get_current_workfile_context() + context = pipeline.get_current_workfile_context() filepath = self.fname.replace("\\", "/") diff --git a/openpype/hosts/tvpaint/plugins/publish/collect_workfile_data.py b/openpype/hosts/tvpaint/plugins/publish/collect_workfile_data.py index f4259f1b5f4..f5c86c613bf 100644 --- a/openpype/hosts/tvpaint/plugins/publish/collect_workfile_data.py +++ b/openpype/hosts/tvpaint/plugins/publish/collect_workfile_data.py @@ -4,7 +4,7 @@ import pyblish.api import avalon.api -from avalon.tvpaint import pipeline, lib +from openpype.hosts.tvpaint.api import pipeline, lib class ResetTVPaintWorkfileMetadata(pyblish.api.Action): diff --git a/openpype/hosts/tvpaint/plugins/publish/increment_workfile_version.py b/openpype/hosts/tvpaint/plugins/publish/increment_workfile_version.py index a96a8e3d5de..c9f2434cef3 100644 --- a/openpype/hosts/tvpaint/plugins/publish/increment_workfile_version.py +++ b/openpype/hosts/tvpaint/plugins/publish/increment_workfile_version.py @@ -1,7 +1,7 @@ import pyblish.api -from avalon.tvpaint import workio from openpype.api import version_up +from openpype.hosts.tvpaint.api import workio class IncrementWorkfileVersion(pyblish.api.ContextPlugin): diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_asset_name.py b/openpype/hosts/tvpaint/plugins/publish/validate_asset_name.py index 4ce8d5347de..0fdeba0a21e 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_asset_name.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_asset_name.py @@ -1,5 +1,5 @@ import pyblish.api -from avalon.tvpaint import pipeline +from openpype.hosts.tvpaint.api import pipeline class FixAssetNames(pyblish.api.Action): diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_marks.py b/openpype/hosts/tvpaint/plugins/publish/validate_marks.py index e2ef81e4a44..9d55bb21a92 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_marks.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_marks.py @@ -1,7 +1,7 @@ import json import pyblish.api -from avalon.tvpaint import lib +from openpype.hosts.tvpaint.api import lib class ValidateMarksRepair(pyblish.api.Action): diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_start_frame.py b/openpype/hosts/tvpaint/plugins/publish/validate_start_frame.py index d769d47736e..e2f8386757b 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_start_frame.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_start_frame.py @@ -1,5 +1,5 @@ import pyblish.api -from avalon.tvpaint import lib +from openpype.hosts.tvpaint.api import lib class RepairStartFrame(pyblish.api.Action): diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_workfile_metadata.py b/openpype/hosts/tvpaint/plugins/publish/validate_workfile_metadata.py index 757da3294a2..48fbeedb598 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_workfile_metadata.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_workfile_metadata.py @@ -1,5 +1,5 @@ import pyblish.api -from avalon.tvpaint import save_file +from openpype.hosts.tvpaint.api import save_file class ValidateWorkfileMetadataRepair(pyblish.api.Action): From 37777f7877164294625102ee96b049e9c79803f2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 26 Nov 2021 16:06:17 +0100 Subject: [PATCH 026/492] added tvpaint plugin code and builds --- .../hosts/tvpaint/tvpaint_plugin/__init__.py | 6 + .../tvpaint_plugin/plugin_code/CMakeLists.txt | 45 + .../tvpaint_plugin/plugin_code/README.md | 34 + .../tvpaint_plugin/plugin_code/library.cpp | 790 ++++++++++++++++++ .../tvpaint_plugin/plugin_code/library.def | 10 + .../boost_random-vc142-mt-x64-1_72.dll | Bin 0 -> 30720 bytes .../windows_x64/plugin/OpenPypePlugin.dll | Bin 0 -> 5808128 bytes .../boost_random-vc142-mt-x32-1_72.dll | Bin 0 -> 26112 bytes .../windows_x86/plugin/OpenPypePlugin.dll | Bin 0 -> 5570560 bytes 9 files changed, 885 insertions(+) create mode 100644 openpype/hosts/tvpaint/tvpaint_plugin/__init__.py create mode 100644 openpype/hosts/tvpaint/tvpaint_plugin/plugin_code/CMakeLists.txt create mode 100644 openpype/hosts/tvpaint/tvpaint_plugin/plugin_code/README.md create mode 100644 openpype/hosts/tvpaint/tvpaint_plugin/plugin_code/library.cpp create mode 100644 openpype/hosts/tvpaint/tvpaint_plugin/plugin_code/library.def create mode 100644 openpype/hosts/tvpaint/tvpaint_plugin/plugin_files/windows_x64/additional_libraries/boost_random-vc142-mt-x64-1_72.dll create mode 100644 openpype/hosts/tvpaint/tvpaint_plugin/plugin_files/windows_x64/plugin/OpenPypePlugin.dll create mode 100644 openpype/hosts/tvpaint/tvpaint_plugin/plugin_files/windows_x86/additional_libraries/boost_random-vc142-mt-x32-1_72.dll create mode 100644 openpype/hosts/tvpaint/tvpaint_plugin/plugin_files/windows_x86/plugin/OpenPypePlugin.dll diff --git a/openpype/hosts/tvpaint/tvpaint_plugin/__init__.py b/openpype/hosts/tvpaint/tvpaint_plugin/__init__.py new file mode 100644 index 00000000000..59a7aaf99b7 --- /dev/null +++ b/openpype/hosts/tvpaint/tvpaint_plugin/__init__.py @@ -0,0 +1,6 @@ +import os + + +def get_plugin_files_path(): + current_dir = os.path.dirname(os.path.abspath(__file__)) + return os.path.join(current_dir, "plugin_files") diff --git a/openpype/hosts/tvpaint/tvpaint_plugin/plugin_code/CMakeLists.txt b/openpype/hosts/tvpaint/tvpaint_plugin/plugin_code/CMakeLists.txt new file mode 100644 index 00000000000..ecd94acc99d --- /dev/null +++ b/openpype/hosts/tvpaint/tvpaint_plugin/plugin_code/CMakeLists.txt @@ -0,0 +1,45 @@ +cmake_minimum_required(VERSION 3.17) +project(OpenPypePlugin C CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_EXTENSIONS OFF) + +set(IP_ENABLE_UNICODE OFF) +set(IP_ENABLE_DOCTEST OFF) + +if(MSVC) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) +endif() + +# TODO better options +option(BOOST_ROOT "Path to root of Boost" "") + +option(OPENSSL_INCLUDE "OpenSSL include path" "") +option(OPENSSL_LIB_DIR "OpenSSL lib path" "") + +option(WEBSOCKETPP_INCLUDE "Websocketpp include path" "") +option(WEBSOCKETPP_LIB_DIR "Websocketpp lib path" "") + +option(JSONRPCPP_INCLUDE "Jsonrpcpp include path" "") + +find_package(Boost 1.72.0 COMPONENTS random) + +include_directories( + "${TVPAINT_SDK_INCLUDE}" + "${OPENSSL_INCLUDE}" + "${WEBSOCKETPP_INCLUDE}" + "${JSONRPCPP_INCLUDE}" + "${Boost_INCLUDE_DIR}" +) +link_directories( + "${OPENSSL_LIB_DIR}" + "${WEBSOCKETPP_LIB_DIR}" +) + +add_library(jsonrpcpp INTERFACE) + +add_library(${PROJECT_NAME} SHARED library.cpp library.def "${TVPAINT_SDK_LIB}/dllx.c") + +target_link_libraries(${PROJECT_NAME} ${Boost_LIBRARIES}) +target_link_libraries(${PROJECT_NAME} jsonrpcpp) diff --git a/openpype/hosts/tvpaint/tvpaint_plugin/plugin_code/README.md b/openpype/hosts/tvpaint/tvpaint_plugin/plugin_code/README.md new file mode 100644 index 00000000000..03b0a31f513 --- /dev/null +++ b/openpype/hosts/tvpaint/tvpaint_plugin/plugin_code/README.md @@ -0,0 +1,34 @@ +README for TVPaint Avalon plugin +================================ +Introduction +------------ +This project is dedicated to integrate Avalon functionality to TVPaint. +This implementaiton is using TVPaint plugin (C/C++) which can communicate with python process. The communication should allow to trigger tools or pipeline functions from TVPaint and accept requests from python process at the same time. + +Current implementation is based on websocket protocol, using json-rpc communication (specification 2.0). Project is in beta stage, tested only on Windows. + +To be able to load plugin, environment variable `WEBSOCKET_URL` must be set otherwise plugin won't load at all. Plugin should not affect TVPaint if python server crash, but buttons won't work. + +## Requirements - Python server +- python >= 3.6 +- aiohttp +- aiohttp-json-rpc + +### Windows +- pywin32 - required only for plugin installation + +## Requirements - Plugin compilation +- TVPaint SDK - Ask for SDK on TVPaint support. +- Boost 1.72.0 - Boost is used across other plugins (Should be possible to use different version with CMakeLists modification) +- Websocket++/Websocketpp - Websocket library (https://github.com/zaphoyd/websocketpp) +- OpenSSL library - Required by Websocketpp +- jsonrpcpp - C++ library handling json-rpc 2.0 (https://github.com/badaix/jsonrpcpp) +- nlohmann/json - Required for jsonrpcpp (https://github.com/nlohmann/json) + +### jsonrpcpp +This library has `nlohmann/json` as it's part, but current `master` has old version which has bug and probably won't be possible to use library on windows without using last `nlohmann/json`. + +## TODO +- modify code and CMake to be able to compile on MacOS/Linux +- separate websocket logic from plugin logic +- hide buttons and show error message if server is closed diff --git a/openpype/hosts/tvpaint/tvpaint_plugin/plugin_code/library.cpp b/openpype/hosts/tvpaint/tvpaint_plugin/plugin_code/library.cpp new file mode 100644 index 00000000000..a57124084b3 --- /dev/null +++ b/openpype/hosts/tvpaint/tvpaint_plugin/plugin_code/library.cpp @@ -0,0 +1,790 @@ +#ifdef _WIN32 +// Include before +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "plugdllx.h" + +#include + +#include +#include + +#include "json.hpp" +#include "jsonrpcpp.hpp" + + +// All functions not exported should be static. +// All global variables should be static. + +// mReq Identification of the requester. (=0 closed, !=0 requester ID) +static struct { + bool firstParams; + DWORD mReq; + void* mLocalFile; + PIFilter *current_filter; + // Id counter for client requests + int client_request_id; + // There are new menu items + bool newMenuItems; + // Menu item definitions received from connection + nlohmann::json menuItems; + // Menu items used in requester by their ID + nlohmann::json menuItemsById; + std::list menuItemsIds; + // Messages from server before processing. + // - messages can't be process at the moment of recieve as client is running in thread + std::queue messages; + // Responses to requests mapped by request id + std::map responses; + +} Data = { + true, + 0, + nullptr, + nullptr, + 1, + false, + nlohmann::json::object(), + nlohmann::json::object() +}; + +// Json rpc 2.0 parser - for handling messages and callbacks +jsonrpcpp::Parser parser; +typedef websocketpp::client client; + + +class connection_metadata { +private: + websocketpp::connection_hdl m_hdl; + client *m_endpoint; + std::string m_status; +public: + typedef websocketpp::lib::shared_ptr ptr; + + connection_metadata(websocketpp::connection_hdl hdl, client *endpoint) + : m_hdl(hdl), m_status("Connecting") { + m_endpoint = endpoint; + } + + void on_open(client *c, websocketpp::connection_hdl hdl) { + m_status = "Open"; + } + + void on_fail(client *c, websocketpp::connection_hdl hdl) { + m_status = "Failed"; + } + + void on_close(client *c, websocketpp::connection_hdl hdl) { + m_status = "Closed"; + } + + void on_message(websocketpp::connection_hdl, client::message_ptr msg) { + std::string json_str; + if (msg->get_opcode() == websocketpp::frame::opcode::text) { + json_str = msg->get_payload(); + } else { + json_str = websocketpp::utility::to_hex(msg->get_payload()); + } + process_message(json_str); + } + + void process_message(std::string msg) { + std::cout << "--> " << msg << "\n"; + try { + jsonrpcpp::entity_ptr entity = parser.do_parse(msg); + if (!entity) { + // Return error code? + + } else if (entity->is_response()) { + jsonrpcpp::Response response = jsonrpcpp::Response(entity->to_json()); + Data.responses[response.id().int_id()] = response; + + } else if (entity->is_request() || entity->is_notification()) { + Data.messages.push(msg); + } + } + catch (const jsonrpcpp::RequestException &e) { + std::string message = e.to_json().dump(); + std::cout << "<-- " << e.to_json().dump() << "\n"; + send(message); + } + catch (const jsonrpcpp::ParseErrorException &e) { + std::string message = e.to_json().dump(); + std::cout << "<-- " << message << "\n"; + send(message); + } + catch (const jsonrpcpp::RpcException &e) { + std::cerr << "RpcException: " << e.what() << "\n"; + std::string message = jsonrpcpp::ParseErrorException(e.what()).to_json().dump(); + std::cout << "<-- " << message << "\n"; + send(message); + } + catch (const std::exception &e) { + std::cerr << "Exception: " << e.what() << "\n"; + } + } + + void send(std::string message) { + if (get_status() != "Open") { + return; + } + websocketpp::lib::error_code ec; + + m_endpoint->send(m_hdl, message, websocketpp::frame::opcode::text, ec); + if (ec) { + std::cout << "> Error sending message: " << ec.message() << std::endl; + return; + } + } + + void send_notification(jsonrpcpp::Notification *notification) { + send(notification->to_json().dump()); + } + + void send_response(jsonrpcpp::Response *response) { + send(response->to_json().dump()); + } + + void send_request(jsonrpcpp::Request *request) { + send(request->to_json().dump()); + } + + websocketpp::connection_hdl get_hdl() const { + return m_hdl; + } + + std::string get_status() const { + return m_status; + } +}; + + +class websocket_endpoint { +private: + client m_endpoint; + connection_metadata::ptr client_metadata; + websocketpp::lib::shared_ptr m_thread; + bool thread_is_running = false; + +public: + websocket_endpoint() { + m_endpoint.clear_access_channels(websocketpp::log::alevel::all); + m_endpoint.clear_error_channels(websocketpp::log::elevel::all); + } + + ~websocket_endpoint() { + close_connection(); + } + + void close_connection() { + m_endpoint.stop_perpetual(); + if (connected()) + { + // Close client + close(websocketpp::close::status::normal, ""); + } + if (thread_is_running) { + // Join thread + m_thread->join(); + thread_is_running = false; + } + } + + bool connected() + { + return (client_metadata && client_metadata->get_status() == "Open"); + } + int connect(std::string const &uri) { + if (client_metadata && client_metadata->get_status() == "Open") { + std::cout << "> Already connected" << std::endl; + return 0; + } + + m_endpoint.init_asio(); + m_endpoint.start_perpetual(); + + m_thread.reset(new websocketpp::lib::thread(&client::run, &m_endpoint)); + thread_is_running = true; + + websocketpp::lib::error_code ec; + + client::connection_ptr con = m_endpoint.get_connection(uri, ec); + + if (ec) { + std::cout << "> Connect initialization error: " << ec.message() << std::endl; + return -1; + } + + client_metadata = websocketpp::lib::make_shared(con->get_handle(), &m_endpoint); + + con->set_open_handler(websocketpp::lib::bind( + &connection_metadata::on_open, + client_metadata, + &m_endpoint, + websocketpp::lib::placeholders::_1 + )); + con->set_fail_handler(websocketpp::lib::bind( + &connection_metadata::on_fail, + client_metadata, + &m_endpoint, + websocketpp::lib::placeholders::_1 + )); + con->set_close_handler(websocketpp::lib::bind( + &connection_metadata::on_close, + client_metadata, + &m_endpoint, + websocketpp::lib::placeholders::_1 + )); + con->set_message_handler(websocketpp::lib::bind( + &connection_metadata::on_message, + client_metadata, + websocketpp::lib::placeholders::_1, + websocketpp::lib::placeholders::_2 + )); + + m_endpoint.connect(con); + + return 1; + } + + void close(websocketpp::close::status::value code, std::string reason) { + if (!client_metadata || client_metadata->get_status() != "Open") { + std::cout << "> Not connected yet" << std::endl; + return; + } + + websocketpp::lib::error_code ec; + + m_endpoint.close(client_metadata->get_hdl(), code, reason, ec); + if (ec) { + std::cout << "> Error initiating close: " << ec.message() << std::endl; + } + } + + void send(std::string message) { + if (!client_metadata || client_metadata->get_status() != "Open") { + std::cout << "> Not connected yet" << std::endl; + return; + } + + client_metadata->send(message); + } + + void send_notification(jsonrpcpp::Notification *notification) { + client_metadata->send_notification(notification); + } + + void send_response(jsonrpcpp::Response *response) { + client_metadata->send(response->to_json().dump()); + } + + void send_response(std::shared_ptr response) { + client_metadata->send(response->to_json().dump()); + } + + void send_request(jsonrpcpp::Request *request) { + client_metadata->send_request(request); + } +}; + +class Communicator { +private: + // URL to websocket server + std::string websocket_url; + // Should be avalon plugin available? + // - this may change during processing if websocketet url is not set or server is down + bool use_avalon; +public: + Communicator(); + websocket_endpoint endpoint; + bool is_connected(); + bool is_usable(); + void connect(); + void process_requests(); + jsonrpcpp::Response call_method(std::string method_name, nlohmann::json params); + void call_notification(std::string method_name, nlohmann::json params); +}; + +Communicator::Communicator() { + // URL to websocket server + websocket_url = std::getenv("WEBSOCKET_URL"); + // Should be avalon plugin available? + // - this may change during processing if websocketet url is not set or server is down + if (websocket_url == "") { + use_avalon = false; + } else { + use_avalon = true; + } +} + +bool Communicator::is_connected(){ + return endpoint.connected(); +} + +bool Communicator::is_usable(){ + return use_avalon; +} + +void Communicator::connect() +{ + if (!use_avalon) { + return; + } + int con_result; + con_result = endpoint.connect(websocket_url); + if (con_result == -1) + { + use_avalon = false; + } else { + use_avalon = true; + } +} + +void Communicator::call_notification(std::string method_name, nlohmann::json params) { + if (!use_avalon || !is_connected()) {return;} + + jsonrpcpp::Notification notification = {method_name, params}; + endpoint.send_notification(¬ification); +} + +jsonrpcpp::Response Communicator::call_method(std::string method_name, nlohmann::json params) { + jsonrpcpp::Response response; + if (!use_avalon || !is_connected()) + { + return response; + } + int request_id = Data.client_request_id++; + jsonrpcpp::Request request = {request_id, method_name, params}; + endpoint.send_request(&request); + + bool found = false; + while (!found) { + std::map::iterator iter = Data.responses.find(request_id); + if (iter != Data.responses.end()) { + //element found == was found response + response = iter->second; + Data.responses.erase(request_id); + found = true; + } else { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + } + return response; +} + +void Communicator::process_requests() { + if (!use_avalon || !is_connected() || Data.messages.empty()) {return;} + + std::string msg = Data.messages.front(); + Data.messages.pop(); + std::cout << "Parsing: " << msg << std::endl; + // TODO: add try->except block + auto response = parser.parse(msg); + if (response->is_response()) { + endpoint.send_response(response); + } else { + jsonrpcpp::request_ptr request = std::dynamic_pointer_cast(response); + jsonrpcpp::Error error("Method \"" + request->method() + "\" not found", -32601); + jsonrpcpp::Response _response(request->id(), error); + endpoint.send_response(&_response); + } +} + +jsonrpcpp::response_ptr define_menu(const jsonrpcpp::Id &id, const jsonrpcpp::Parameter ¶ms) { + /* Define plugin menu. + + Menu is defined with json with "title" and "menu_items". + Each item in "menu_items" must have keys: + - "callback" - callback called with RPC when button is clicked + - "label" - label of button + - "help" - tooltip of button + ``` + { + "title": "< Menu title>", + "menu_items": [ + { + "callback": "workfiles_tool", + "label": "Workfiles", + "help": "Open workfiles tool" + }, + ... + ] + } + ``` + */ + Data.menuItems = params.to_json()[0]; + Data.newMenuItems = true; + + std::string output; + + return std::make_shared(id, output); +} + +jsonrpcpp::response_ptr execute_george(const jsonrpcpp::Id &id, const jsonrpcpp::Parameter ¶ms) { + const char *george_script; + char cmd_output[1024] = {0}; + char empty_char = {0}; + std::string std_george_script; + std::string output; + + nlohmann::json json_params = params.to_json(); + std_george_script = json_params[0]; + george_script = std_george_script.c_str(); + + // Result of `TVSendCmd` is int with length of output string + TVSendCmd(Data.current_filter, george_script, cmd_output); + + for (int i = 0; i < sizeof(cmd_output); i++) + { + if (cmd_output[i] == empty_char){ + break; + } + output += cmd_output[i]; + } + return std::make_shared(id, output); +} + +void register_callbacks(){ + parser.register_request_callback("define_menu", define_menu); + parser.register_request_callback("execute_george", execute_george); +} + +Communicator communication; + +//////////////////////////////////////////////////////////////////////////////////////// + +static char* GetLocalString( PIFilter* iFilter, int iNum, char* iDefault ) +{ + char* str; + + if( Data.mLocalFile == NULL ) + return iDefault; + + str = TVGetLocalString( iFilter, Data.mLocalFile, iNum ); + if( str == NULL || strlen( str ) == 0 ) + return iDefault; + + return str; +} + +/**************************************************************************************/ +// Localisation + +// numbers (like 10011) are IDs in the localized file. +// strings are the default values to use when the ID is not found +// in the localized file (or the localized file doesn't exist). +std::string label_from_evn() +{ + std::string _plugin_label = "Avalon"; + if (std::getenv("AVALON_LABEL") && std::getenv("AVALON_LABEL") != "") + { + _plugin_label = std::getenv("AVALON_LABEL"); + } + return _plugin_label; +} +std::string plugin_label = label_from_evn(); + +#define TXT_REQUESTER GetLocalString( iFilter, 100, "OpenPype Tools" ) + +#define TXT_REQUESTER_ERROR GetLocalString( iFilter, 30001, "Can't Open Requester !" ) + +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// + +// The functions directly called by Aura through the plugin interface + + + +/**************************************************************************************/ +// "About" function. + + +void FAR PASCAL PI_About( PIFilter* iFilter ) +{ + char text[256]; + + sprintf( text, "%s %d,%d", iFilter->PIName, iFilter->PIVersion, iFilter->PIRevision ); + + // Just open a warning popup with the filter name and version. + // You can open a much nicer requester if you want. + TVWarning( iFilter, text ); +} + + +/**************************************************************************************/ +// Function called at Aura startup, when the filter is loaded. +// Should do as little as possible to keep Aura's startup time small. + +int FAR PASCAL PI_Open( PIFilter* iFilter ) +{ + Data.current_filter = iFilter; + char tmp[256]; + + strcpy( iFilter->PIName, plugin_label.c_str() ); + iFilter->PIVersion = 1; + iFilter->PIRevision = 0; + + // If this plugin was the one open at Aura shutdown, re-open it + TVReadUserString( iFilter, iFilter->PIName, "Open", tmp, "0", 255 ); + if( atoi( tmp ) ) + { + PI_Parameters( iFilter, NULL ); // NULL as iArg means "open the requester" + } + + communication.connect(); + register_callbacks(); + return 1; // OK +} + + +/**************************************************************************************/ +// Aura shutdown: we make all the necessary cleanup + +void FAR PASCAL PI_Close( PIFilter* iFilter ) +{ + if( Data.mLocalFile ) + { + TVCloseLocalFile( iFilter, Data.mLocalFile ); + } + if( Data.mReq ) + { + TVCloseReq( iFilter, Data.mReq ); + } + communication.endpoint.close_connection(); +} + + +/**************************************************************************************/ +// we have something to do ! + +int FAR PASCAL PI_Parameters( PIFilter* iFilter, char* iArg ) +{ + if( !iArg ) + { + + // If the requester is not open, we open it. + if( Data.mReq == 0) + { + // Create empty requester because menu items are defined with + // `define_menu` callback + DWORD req = TVOpenFilterReqEx( + iFilter, + 185, + 20, + NULL, + NULL, + PIRF_STANDARD_REQ | PIRF_COLLAPSABLE_REQ, + FILTERREQ_NO_TBAR + ); + if( req == 0 ) + { + TVWarning( iFilter, TXT_REQUESTER_ERROR ); + return 0; + } + + + Data.mReq = req; + // This is a very simple requester, so we create it's content right here instead + // of waiting for the PICBREQ_OPEN message... + // Not recommended for more complex requesters. (see the other examples) + + // Sets the title of the requester. + TVSetReqTitle( iFilter, Data.mReq, TXT_REQUESTER ); + // Request to listen to ticks + TVGrabTicks(iFilter, req, PITICKS_FLAG_ON); + } + else + { + // If it is already open, we just put it on front of all other requesters. + TVReqToFront( iFilter, Data.mReq ); + } + } + + return 1; +} + + +int newMenuItemsProcess(PIFilter* iFilter) { + // Menu items defined with `define_menu` should be propagated. + + // Change flag that there are new menu items (avoid infinite loop) + Data.newMenuItems = false; + // Skip if requester does not exists + if (Data.mReq == 0) { + return 0; + } + // Remove all previous menu items + for (int menu_id : Data.menuItemsIds) + { + TVRemoveButtonReq(iFilter, Data.mReq, menu_id); + } + // Clear caches + Data.menuItemsById.clear(); + Data.menuItemsIds.clear(); + + // We use a variable to contains the vertical position of the buttons. + // Each time we create a button, we add its size to this variable. + // This makes it very easy to add/remove/displace buttons in a requester. + int x_pos = 9; + int y_pos = 5; + + // Menu width + int menu_width = 185; + // Single menu item width + int btn_width = menu_width - 19; + // Single row height (btn height is 18) + int row_height = 20; + // Additional height to menu + int height_offset = 5; + + // This is a very simple requester, so we create it's content right here instead + // of waiting for the PICBREQ_OPEN message... + // Not recommended for more complex requesters. (see the other examples) + + const char *menu_title = TXT_REQUESTER; + if (Data.menuItems.contains("title")) + { + menu_title = Data.menuItems["title"].get()->c_str(); + } + // Sets the title of the requester. + TVSetReqTitle( iFilter, Data.mReq, menu_title ); + + // Resize menu + // First get current position and sizes (we only need the position) + int current_x = 0; + int current_y = 0; + int current_width = 0; + int current_height = 0; + TVInfoReq(iFilter, Data.mReq, ¤t_x, ¤t_y, ¤t_width, ¤t_height); + + // Calculate new height + int menu_height = (row_height * Data.menuItems["menu_items"].size()) + height_offset; + // Resize + TVResizeReq(iFilter, Data.mReq, current_x, current_y, menu_width, menu_height); + + // Add menu items + int item_counter = 1; + for (auto& item : Data.menuItems["menu_items"].items()) + { + int item_id = item_counter * 10; + item_counter ++; + std::string item_id_str = std::to_string(item_id); + nlohmann::json item_data = item.value(); + const char *item_label = item_data["label"].get()->c_str(); + const char *help_text = item_data["help"].get()->c_str(); + std::string item_callback = item_data["callback"].get(); + TVAddButtonReq(iFilter, Data.mReq, x_pos, y_pos, btn_width, 0, item_id, PIRBF_BUTTON_NORMAL|PIRBF_BUTTON_ACTION, item_label); + TVSetButtonInfoText( iFilter, Data.mReq, item_id, help_text ); + y_pos += row_height; + + Data.menuItemsById[std::to_string(item_id)] = item_callback; + Data.menuItemsIds.push_back(item_id); + } + + return 1; +} +/**************************************************************************************/ +// something happenned that needs our attention. +// Global variable where current button up data are stored +std::string button_up_item_id_str; +int FAR PASCAL PI_Msg( PIFilter* iFilter, INTPTR iEvent, INTPTR iReq, INTPTR* iArgs ) +{ + Data.current_filter = iFilter; + // what did happen ? + switch( iEvent ) + { + // The user just 'clicked' on a normal button + case PICBREQ_BUTTON_UP: + button_up_item_id_str = std::to_string(iArgs[0]); + if (Data.menuItemsById.contains(button_up_item_id_str)) + { + std::string callback_name = Data.menuItemsById[button_up_item_id_str].get(); + communication.call_method(callback_name, nlohmann::json::array()); + } + TVExecute( iFilter ); + break; + + // The requester was just closed. + case PICBREQ_CLOSE: + // requester doesn't exists anymore + Data.mReq = 0; + + char tmp[256]; + // Save the requester state (opened or closed) + // iArgs[4] contains a flag which tells us if the requester + // has been closed by the user (flag=0) or by Aura's shutdown (flag=1). + // If it was by Aura's shutdown, that means this requester was the + // last one open, so we should reopen this one the next time Aura + // is started. Else we won't open it next time. + sprintf( tmp, "%d", (int)(iArgs[4]) ); + + // Save it in Aura's init file. + TVWriteUserString( iFilter, iFilter->PIName, "Open", tmp ); + break; + + case PICBREQ_TICKS: + if (Data.newMenuItems) + { + newMenuItemsProcess(iFilter); + } + communication.process_requests(); + } + + return 1; +} + + +/**************************************************************************************/ +// Start of the 'execution' of the filter for a new sequence. +// - iNumImages contains the total number of frames to be processed. +// Here you should allocate memory that is used for all frames, +// and precompute all the stuff that doesn't change from frame to frame. + + +int FAR PASCAL PI_SequenceStart( PIFilter* iFilter, int iNumImages ) +{ + // In this simple example we don't have anything to allocate/precompute. + + // 1 means 'continue', 0 means 'error, abort' (like 'not enough memory') + return 1; +} + + +// Here you should cleanup what you've done in PI_SequenceStart + +void FAR PASCAL PI_SequenceFinish( PIFilter* iFilter ) +{} + + +/**************************************************************************************/ +// This is called before each frame. +// Here you should allocate memory and precompute all the stuff you can. + +int FAR PASCAL PI_Start( PIFilter* iFilter, double iPos, double iSize ) +{ + return 1; +} + + +void FAR PASCAL PI_Finish( PIFilter* iFilter ) +{ + // nothing special to cleanup +} + + +/**************************************************************************************/ +// 'Execution' of the filter. +int FAR PASCAL PI_Work( PIFilter* iFilter ) +{ + return 1; +} diff --git a/openpype/hosts/tvpaint/tvpaint_plugin/plugin_code/library.def b/openpype/hosts/tvpaint/tvpaint_plugin/plugin_code/library.def new file mode 100644 index 00000000000..882f2b4719c --- /dev/null +++ b/openpype/hosts/tvpaint/tvpaint_plugin/plugin_code/library.def @@ -0,0 +1,10 @@ +LIBRARY Avalonplugin +EXPORTS + PI_Msg + PI_Open + PI_About + PI_Parameters + PI_Start + PI_Work + PI_Finish + PI_Close diff --git a/openpype/hosts/tvpaint/tvpaint_plugin/plugin_files/windows_x64/additional_libraries/boost_random-vc142-mt-x64-1_72.dll b/openpype/hosts/tvpaint/tvpaint_plugin/plugin_files/windows_x64/additional_libraries/boost_random-vc142-mt-x64-1_72.dll new file mode 100644 index 0000000000000000000000000000000000000000..46bd533b72dd36a9fc88846fde23d3295d826ce5 GIT binary patch literal 30720 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 U_tb#C1SqORy8!>y{e>L(AJhZ8`2YX_ literal 0 HcmV?d00001 diff --git a/openpype/hosts/tvpaint/tvpaint_plugin/plugin_files/windows_x64/plugin/OpenPypePlugin.dll b/openpype/hosts/tvpaint/tvpaint_plugin/plugin_files/windows_x64/plugin/OpenPypePlugin.dll new file mode 100644 index 0000000000000000000000000000000000000000..293a7b19b01076f4b076e9dd870b2ce7de1ab8d0 GIT binary patch literal 5808128 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

RvnJ_4)S?o-LM|; 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

XJu8{Y!Ph2bVcL$P2?n6i3Au)h5*nO*% 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�Byj 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

E{ZJ|!-Y`s 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 b^l1p1lb%tYaVVpYzt028->&~pB=CO#lgmzQ literal 0 HcmV?d00001 diff --git a/openpype/hosts/tvpaint/tvpaint_plugin/plugin_files/windows_x86/plugin/OpenPypePlugin.dll b/openpype/hosts/tvpaint/tvpaint_plugin/plugin_files/windows_x86/plugin/OpenPypePlugin.dll new file mode 100644 index 0000000000000000000000000000000000000000..9671d8a27b1c2cc1f1f213328c68a3dee21a58f5 GIT binary patch literal 5570560 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

@0%hbjMu>i{RuVe!V67uakpynKY@}~ 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

*=c_=ct)IM^2)!#8f3PRjzPdtV2oV4IKi) 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

eAvD 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 Ku`Vn{MUzI;*;1E^gpGO3WZ$bjK`| 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%0DsWY9gCf@DscrbRCqxsC^H~G)| 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⋙&0}jI77lx^JZn%CyjAUXcwYVWZlgU;s*Fx;~7? z7nevz5S^EJ2yQg~Naz6#@==WxK62+)L|W1y4I$OLsi2svc@*7kBw^?9PK4Qnv_GxU$hFBF?bNNwYv3=1 zHIm`9pe)O@JA;QZTTz^PMvg|VxqO%JzY^>g}g%&g9t8`w@{&s_)Y>LV$& z>1&Wg;H*|ix_)(*(>ds89MVsw7UAdm%gFs1spNpxH|sF+7d%Tl`B}cpsJnZrY{lbY z`sz!Hybm~QdF<)CK!c5U3E(`P=4Z~mTl|`os{!mRN?$W!05guLTYsIAWH((l{xXt` zE*x*Ub|^Gvdow#&FPGl(MG`wX)QluMFhJ9ebgeZgX>L#5xwo*P(SVAee%Ws<@XH-b z_|$dqCDddTppA>_ZPC_+Ze)N8R)R@X6NM(1(Qve1l}=}gSD8JT0^ zqq^TE?{rN{PTx~~0rcq9WYkGHinN2jGtZ(CXq2~qg{uge zC?QK;na`W&t>pPnj6mB`enj@C>VvP1$gOaZPB}FKjb(b%J~e4vo^xtO^8%M60WLA$ z;C~vU9=C6;1BRJkH{?)FW}a2a{JM1Sf!dPJ9D8pNNsR6{gW9E}yohWT7EpjANeWIQ zQfm}x9Ygfnh}11F`B5KxOWJ33Kh{R2)6-v-Y#4odQO1}%zBc;37nL(6sxQY_^mPh@ z8(8#uZfGId&YXRhl?6#HZ-cWp{QMV7M$ptuJ^OwbSty+P3!1SS1@F$Wn02VybcvF7 ztl{s^PdLJj>%C5`{_ttx!1-Ne(?dyC*^>myhKMkNht(Gqf_!t^&=6(UO;V~P#O_%F zn*7(k=f744_&O|UXbALJ3Dii&c0yHK=Hj8~p;*`et6f^j?rPVSWn^sMWo-XReFxIo zv^t@zox=`V8?5jd+Yi868tcnBkeN!xSk;jZwG*nX(brX(bh8FTw9- z3n|#?81NOEt;w)xE5ibtiOhcg`EP0fm27eRw{+UDE{{k_#{4$=G^#Tou&fZwG{;&G zQD2ulf$hA!B8afR{_*JrIC5|ygn*RuoCuh@@!<}C%X=KpuEPilx26Aaui0=S0dEt4=1v<=9eoyF3M>@lwVzPs`($MXjxhYal<9PJsIlw{T&F#K3`nH0XG z1<)cfKGWvSwijXZ=`tLBQ}ItV+izRe_; z!A)Db*|Rq5qO{WW>@BBmz}k&Sycz1Jwj}SrztW;CCY|x{b!QHMnBy1W zg;S53>BxWrA2=_lV`O;WWq1V=7MLBnR39A#m!y)k!Z_a44++aV{Qz^AZ$2cINW%px zteVbgXYK$9)igOqg#K4lYuh~+a2mcWqZId<%uPD-alw^WW{i@_T{k57%}t`yrwof3 z7DJng(fFg%tb%%1RKgmn#bOn%sbMBX7ODb0&RO7^az=xJIv$a80X4F%g4akdlUll= zi=F4+pJ&iSY5&47&urKU&dlu1I5@-1fNI}X1Ij`k4x2C> zqagQlt2&9{2*VL@1G2=9jcS+Oa0Dgwm)6D%wHj*OE4AtzbDKdKt%%-yNZN#kq76m& zfubw8w|Ymi#xJ~W5b_)SF!NG&9(pkvx*Iv(ocfEy|6vNi-qJF@= zX9O*4{#r^wFic_fzxh`yOi^8mua}WGx2NvhgDM*Z-tq-XuKmVp;&>@yBRyADmZ9z1 zfjcARG(lv%69rIQneDgII_NZ4or_?v&S|Db7y_iWQ)RW!K@;HcrBn5r)W|cnxH31{ zzQb;r{Z$$JCIAhMlTVGK08mx_W1LJ*CHuRzluDNUiJP5hypoJPx})Ax=S|fnu2gQ; zJEhIg%s!+;C7LlZU?cmOVXY0o2T)Di_b0}1bTXmG)vP6H=zwL!q2?_@EmfDc3neuh zZn-3(83ktUxy#5BwnO<&CEO*Xp7^1F!|h2AZ%oX4l$r+f?cgxqj0AnS`E+6k`&bFv zNYfYQ4!0*YRRmL~b6Od{NvDr))>l?4!fe<0HPY}{rD5X4r;2v+-gMo^ZUU$lykX z-c=9l%YFog`jOvisNZkBr}j4W8@b%JTpp%!GHAm*3-P;uwZlfXQ#-K&*={&!=U#?P z4l?&c+xJ7e!MyHLLtCpTG0hQY$fT?_!BU^onC3TIn{D#|XLHJl%#_Zf=Y3B|Q1f=sPd-_!XkH|rXZI5O0C zaMWi+lUe~`>XTFT$>Yr`@XMI`4ocAp}P3)=fv8T!jH%5?hL25|*SV`LmRVWc{ zWPZX?bg0_z!)SbQd39}#M&l3X^ydxx^CSM;z+S3?N1(H)HR(dx{xr?Qa6NxIY1g}A zMB}lG#?pyPhVV>B12=G3b!IeKS>8&4_E?H3s4P+f0ZUd8T0o@D~hm`-x!Ph^d1)Fc5d zWC$ciXoBxl^#q&~LRN9wY+u2QvO~F`2`cduNraCMo2^P_p2rs_rNf#BrOm= zq_C{2UlPXE-gj3!*m{q}ooPyI@#-;TXUL8kv1{E(B`-8XLxzSjX{g3E`RpyHZg945 zAXTGB?L?1K>gC?kV2V2XYQqR)ITyO;GVL%!T!D2wCRsJ&O5JycObnS2EuXYVp_O(#yP~(}+1E4u^o$Uhz;5Qalcn=QJ*vb%_xtJ&4-Dri5Cp;M^3Y=wVDwcLs zu~g-X58QA)k3wslQAL#_KFtmPveCsL57-h>Z*16MM)_)Jj%`)v?TFTms^CuTRTK~n z9}3n~ycEblfBxnMwigXpLbsxG46|;aFnPDf{+(O}DOCFt)f zbEbni(<7GV!>$5rk%^(8Y^Q-};%o)kXUy;zChHqgh})Ggfk4GRsT>h=s4XFiRlqu^ z%ir|{`bJ9C(_bQxqe_R0RT>$Cru2r4Cu(F^OS1}HDwa!k650Mr>u(yzReUIHt7@Im z{yt9KnFDuL{35(?>al(qLTvJZ^AhBYi~rzU{GFug8KTbUXWnVqG^D~|x=<^}@pVL< zlHP_q4S7;NIo+chH#u(b&8AU>4z%jzSzq7j9qAgd$>R#$Rwnk62roC#K6PRnEZWS@ zHyl%|Y(Up}!e}o$xgHvFOrI4Ub6c3(q7Be?c3affq5{{MGDnLH*oP55TG*jasaeJT z7X?LWyjVxh^W3$c*Rz=$gf`%jFRX{#jnC=BG0XMBL$(vybJqzYJCc_keT`N^>oD>c z9MH^ub(Yh)%km2HyfWR1<+3f)hV6 zLqvpn$cx`I<<}uzkkvIvbEQH-Xc0d~EhE>WRW$`Y{{Ur3>P0N3C?dTFeES-@w!|U# zIUn&NLscwkvS-otAg*>pp~pg@4fTfgGV-?npMc&X3wurmwU&=6V3WsTrNqoK|F&``tCP{V=UJ=K*a zb6Zs_94()oT2e_DcUq?=Xx{{yAybOI@C;nfUs{i`ok!`iyLFzlLZ*GcK@GtTnc$jS z(px7lt2UT@xb-beMMOa@&nHHV+@FyW6Tv3#^S(UPbX#nzy0l%0#_VoB)g+?gFit0P zazyjYG&4g;vMF?XiLpH+q>f?=TSCi-G{*yO&Ldg~IMa7~GDo=9nxxu9t3o_3Z8{z_ zd?V73*OW~kkDpF{fP`#Z{)UiP!rYD07>Ihj2p_k`1*<6kMmY7qX0K=0q!CU=I29)7 zK>`&vW^B;1BXBbX7gJCBn*z5r??MySWGL<2$<|qhlFs1F!lN?h-k)>dwP|RC*iDxn z_p*`mVOK6(#9<0GW>!*x%SuO6N}#3J%m}eA={3=&nxxn2gjhLa(1;@=j`Sep<>Tu{ zr}BeH6~xDV4Rvk)nJfygL2Z8v$R|BcO)aSc6(svqvF7#}KRoQS)TuRanI`$(Puafk zO#IJM4kjaEjD#W8JW;bdfmkrhT( zJmF$KaBZZM>_;X?egr@(YY&*_)5#kUh5f)9!_N+^84ggR=H8^$sO-mtglBjIIAD9s z#-)@@3@Z`Vs0L4d^XAMgByrf^yu}L8IMMscMIWuqX{5)uuF^O;^Z6!3PWAZOz%_I0 zFKq1Y^Dz^hC&@M`sq@@q6;B+IJ=LRA+l&2VWZ4uLvqqMMJb)x~i;XOs`N71U@*D4= z7g2}8^5LG#vI-$CBbAI)Vjc@4mG)aY)q;)KC#hs?LTUL8#3meO?p@(c-Q$Q{)AWE= zj7oFtyDn$XJa%K1rYlv`9hF<8v35CX~&(G1AnHHc^H(;k!%g zuyQ{S^4N9K)Y!%QjyxE3qc`ftE<+dQ*n4yAZRem?k3MdAI%JF5lJuxaOo4isF{bVk zz|$Qh$_;KCitfUGFQ~qIsrtJ187)_%+#D>JfFM|Zoyc35y6i&ZhfQYu;?pJp?UHRTJY8!x*d&Jb+6>ha9 z()hZ8JKf4yJCwT|3*$=(+|ZxDp_N~f{uoVGmbX&!E9u{taqi2*Xnb)AkTd0i9*sXL z6$T_0TPQe4JC_^V0PV_FL*;#kq#7iZccu#%dNuUgR4VUM!^rRs4a(|keGQGIL$ld5 zIeSwOQkX=&Plwe8Vbtpnzojg9s+wy7J&>s0iSyXx+m^_;H3VEfwS%y3v0-^KjduGmrm7}cEc&%c?()gr0-FwUP&)#X0=h%bUNb^Dd7<@46+ZfX?{;P zWDukFR8xBlsSktHjY`xrmB`o&JGU1a_M0#|)v$2$42K;GhZ&`;Yo*Na${=~AtX3De zllRHoTfW#y`HL>f@i=v7&U%i{wa$wby|ONa-g4&KNE&u^85X+F1ZKVNoln(Y(E1W( zyJlM^zjU|Q2Cpi!>^a5%v(sEt-mW>1Tzy zJ_{E*hEyJwx#zaGT&*M9nL4Wo13ui|NxH%>M1U^&;b`>v%hO-~`1ImN)~Az0-2`l) z{f(WM4yRarqm5CE;}m<&Ll|Nci??$%cP8$uKcC_psK!bBF!gb$R0w<Q&f9Dg6-t$E`RCVn>F5Tzzo3qR3)5(Xk|6i(2?5tx! zS@ososRQX?5w7#$^7{JYWOhEgIG;N1$Jy28>6LRnd;V}Uajpijh>Pc|vy01*7iVXu zH?s-;-<^DL&(E&TK3raWe11M1#3C+1Gn3Ql#boMU&OY9pUwycFzB&JJ`F!J?UQcGu zAQo}uTwl#@&MvNRKAv4T(~IZX#q;zGEa85bOr3#{)Ah}nGjZIT=Tp#-GrKs&^slGS zS68PuH|Lk`Fc|yh`TX?s>Kxo~etC5cX1+SRJOhEbA1^*$ow<`ikdp&3dVY58o?lEc z{h53DVKTdX{)i9GXO4S$Gl)e@PtQKMr>=YT;d0`DlCCFbXE&3Nm(!2e=QkfNXWcL2 z9gQ(!UB&+4MXz?aex|+>g-SD5;1czBEN{Q-$@#@>_Thv3!Fis5$DdCjf-kVD%d4B| z^UcTYRkb*aL`B)S5nFsmywZlfd<)O99j=O5^QoN3=oLtREJMO(XLv?c1{rwsul_n9h| z+|#oOoH3^}=WH^&fG6qs$~l8G$+@^bg*WNaXiIU(#ZXrX>dF>N(Hi|7E$U;{9ZJgm z(_e7;Bjh$`9cCZNS#OJfz$dT`4u2_IG=Dmg@z|+5cO&L2==F?AU1h8+9qxUdZ8k-S z0A_5y4$L-J?D3&4Lfi}LrEqpOxwyW*fh!0%!uezd52OpHFpTP(*~RSndh1d!C%OM1 ze|*DddC|-v_2!UNcDs6>eY~FCz&ACyI(Khor&IUi^_AneSJU&0>x*sE(41s%PSV_N zq!jiw@kx8TA$?&sbFU`|O-(P)5z)E2zPPvo*CA$gc|O|?78-WzJ-gZe2f0+}TTi)@ zOt`{W+f?Ycp4m{L)8A*Ybt)eyod~3PTgH=VisYJm#)I+}=bfx@VV-*yhP=;4gjl zwalq}3?tW>zQfM*C*LjpKuiSG6QKA(WrMWy(`@`>+lx&t6-l4kjI-7@*aw9`aLcf0 z)jTa}d48h9PNI^O${N$+I(qd3FM7Aufv5Il1Ie-<)=QOT@0M>7Lm)fWng!0nRbTYy z=raJ4`7^H|5+pC4!EP3G_UupJt(6lXuh2SsjiQzMW6Tnq(rAa`St3Xt+Dq4+qM!Gt zlh^lW@9YBEy+!VcRp3t${>4ElxMzEOP(K{oNP~ z>6-2mdke{>>|51wTAEJ9IUrpevKBP?>7+PoJK1iC2AQjE(|uhq1_-C*lE6;%0*$tZA zsG-_BxxR#H|8Sk}EN}eDx}@#{ya0em7XOY#H(wiW0V`JTgq|vL^fRlN?va##{m(D= z54Vr>3sP7u>&wIF>9>aw{(H3l@A&h-{%7>?@c9lY8GoVTfpmIy!?+7vI7}h6SvDVT zlgrd~lI(W{|Njm)wSf*%cmmXtqhG5938iUrOZCg4s;N8i7S0^X2K6;L+N)NJx`yJP?QRX_ z3ucrxl=#{1)=<7)Mp;9NpY3i9<;!K1HI(?-?$%JgT1HtziJ$Fm4dshvlr@z2+3wa* zzE(zALy4d5ZVly2Wt26P_*uy{v{<k_69ynoV$4y8cBW))GH6#5zZaiN#H{IkqN2v_yiaO04CpA{1gR@iRlLhFFtu z1WbC7#Zdw-Crh%H&y`n*wZzZL5o=#Fd>Kn?QXmk#?{m&~lKw%*HOTpwqZ^aI)SLVz z_4X^DXRQd75#|#Q+VcO9@ay*H)7ErF@P|u_Rj|J!r%9vyDM*nQd{jQCp;iAO%P{z9ZO_ zods?;w)dwySu5$QoZrULWMz3PC7X9Fkzp-lizwgl9Gdv+1c?%PE%EsBul5f=_-?dAfA+7~^vZf|>pYrj0b6&Byg`f~4`E|8^%4Pp8DzX5`jbx@GE;mer+eGMG1Z zAc4b7mX;y^)L+K=IB`NI%fLjks~u)$=Kd3zx$$G$$+~G{) zR^bnMs&uE@-ykhYT_m4tr)k`n>m^$CinMV~tF{x`tGO3O`5ELyLe26?SUrU>;i20w z)UReE;@2%il#ob|=`eJ%X48R!hyju!0=dW*pv@@RpHT}bq4MwAmo-}getpb4N?W_d zA9FWDs@~@a)mI-3wUsmodDWDQVzG-CAj2@wzoo6J6mJShe-GCK!}aGRNaYY?_61@6 zW6R`}lFt~aa^Z{*LA>!Jgn}(q5k-j^5w!N0P_lXM8kJb2f5mcnWj@=$02Y8g&(J&? zB;(=tr1lBP&9;ef&4;=nWMq8Be+RXoW}^4u_TWyqo!exfG%j@tlkriM@o|`xZ-Q0* z3RWE;mD#^O+}gh$)E#R7h9&)WSWAMUV=J0YQUC@8Zrfcpg<6fqTQ0{Apteb@TMRCg z9)Sjw@yQ_&L7Hl^T<%{ZM2o(yFx7TuqekzLkBH3CLp>rC(E9T?RQpT*Y17-xd|9db zz$b1Rrw!JG>a`TK3!iu<|5bWn~DV!ups z1nC$MgSOFAc>aEM%Za;LLnXqhMb(Jg*I&xt>&3I56Qi(zBs2|HW0^DstSjo#&udeL-8WppUntXNITm+r>qRZu~?OYr9hsF zj&K%A;56_cq*`#^;e?oX_K!<6f&~1PzhamOd@nD7h9z8#ay!gkCFnfKo)GX+qjVN6 z?1db^?I5wLoXEGd0N=FNRREQ1Y6UJ(+>+R3r{Gr$FPt<6q+7bt^tpvmen$4(d517f z0^?TcoGl0(roqMm3D;TJ9QZ_=l#wU|6oj}C86r{_SmN>_RIR!195LJl!d)?(&XMd0 zZq4?gw-ACA`cqQ%qS;&M6fU749dn|d{p$t`9{#56lH|6wcMUAQmnB2xI@A{81@LcR zL_5y>5>oC6c4enj7w@l7&AYP%ScZny5J++_6#GC)$)-GzjUVd`J84n?MD>LwLpIi) zo2Svh*4C+x7jcS(I@y~>nd?n+eyUBjVMcuClU+IQ&=d(iR$3x$+dH7^rmK(_tC(z& zYlSP)N%C*LFN4yyINYLuE8a>JLpBuBiXnDl4ZV8HduaHns*EIgh`k*z)~2Vml{mKY zLysW0k6J$iY_Qx1JA(T(PH2;#9`Nq$^2*HK=fFii_3mI)lj5Z&>F{8F#)hf*5_U5g zyLJ&3_XEYi)$xd%?uhsc6|1!nbsE3>`3{;+xg;Lrsxk8aQr|KPDde^?V=;&LXX+GM zlA$LeJIV_ldn-L( zDYD&@Crvb`NUCtkgAwDl57F=sRabJ5ba<08wcn4Y7lpFiopqxK+=U;xb_|}~u^?>9 z%rOUSOuH5vG;f5Vs;eJjLyV%w>tNz))zzjta=ZuO+nGL|m|Hp*gm35des)pgXCaC? z1N}ZEWy~_$B^}tOBrGaT>R;Mb-AFlgYw6!=(h~Ow&Oun&!##&?6H|yr%e|p2-RvX@ zg{EEUMg5t*AjtZ&N4FVfET*o&Vq;5jit1B2>W`;}?zu(St%aqH>8BSEn`W(8blL@` zUROmE-?EzPjI%Kz1booSAw<6pSH>mNNpXLU(CdSuLh(cC!bK5MuQQEyL-fLqh}%nv ziRGEvh1Ndp$vRBsA7~2m8@Ml}YQ|p!T0`d+h|_I5S>-=cL}gSW{nHdCg!mY}bjl6b zmXZmdE3guF&b{KbDL{ub0dJ6dgtm(>nBuXc+;?1i#$yTU6Sa<3m1@fFP-6jf>*vNmw{04{BOMRk${5kTvaR4|i5IgV=5{K6 zqWH9>gH{AEq^*Mkp(WZD3Z$srGsvZdvx~bZZ;bk(Q8HWB zsLfL3GDdAkD_f&>imoR-7SsyXTh4q-I9>czsP`N}^rsTExP5@OqPR0okv|p>V?B_M z=>s*fy}FLmxK^$kEU8a?(Ob}>79Og&MeW1r9w9CLn}w-D57!*Jcwm18TY{mS+L;^k z<^J;*YNd<0<_#El;rcwkaXW^%8;!o4Jx;y%*^2dTKR;*7WIwj2$ViRs7nmlw{ojj* z3H&u2JB34hv41-|JEPZg&`dxtzAq;fu>lRtaU5&9> z&F*Fru4Z>}RGWl&~{L_!{_2sFB2!t>0Yrk5Cb;{>okMTr44d9^mpzZ&UoW%X^2X za407iZ(@q&x)WrFzL|l$GlDE7cbU*oFL(9yDOJBmmPb{6h?N@bl5$(z^?|9ODSePE zRTQp~+o1!+Gi_M1H^u7iZr&C}D{zF!FzI}K44n=OGipvh)g0lE6HX84yIkpxGdg7) zSPPg!&3n#eBb~$y6+ceNt(~6!zRd=9Db=+cl86%1KZVgX=c5%G8>VL%D(BNL=qlkJSwm? z7DTo4B&Al>vDlPTFJo7IcQ@wNn*>XAcZcyeqGJ$e^Gjp+mU<*N@N#SU5+H~@) z;xdXG{k@_uQMi7N-dCh-JimiqV4g${fRO|HhZnuZ--2*+dt&oLQP6~V#0{^&DAd>s z@izK9^}e}(#Gfm4{1R`5CNe>bHbg6INE#sK-Rq(`y3c+Y-E)-&S`*@WOxu1hmvSoR z2Rt9z){UD7(+J)(uN@*`OI$syTs9R+q6igrrm+p_w@S6fDtYT`2$p%idv*ycRt0VRGCyv9jPLLnlmdqRC;<+R^gs= z(Ug>xNl0m2`^1VzNv7~GVMI{&ZLpfGR;;xoRJ(iG-wa~(U(z*@^VD*JB5(?nZQZSf z=3@HZCdHEOYA-$BdNw01A{hwk5U%oP@%fn&v%XXQMR7Pjo{PIb@>hiyC6Z9SJls;= z!~JhLIT#uhx?RIW_>IeZ-`M@mLjzMUq^y9c^_bFDr|xWyS}wZu35{gp!*eTwo8luy z9ksT9ivD$x3nW17{suS+E1lcSso!?r&rhkqNnF(P4%EkG*$+k}NCBL{HOQ z(8|MBq-oK?7Rw%&V@tdMyx~e+6Dl;pa<#0F|aUwIiA|rx$WL7dTGzcx; zLFBr$VVJwT;G<#mj>C1JLA@f*l@=LCSPCuU8n})Z8fLEJI9%!B&WPUc-;Zjpk*?`xD1p9Fv zfsd|8rN^ki!U>{8j9#!S7;SZM%Vf#Ey~qzaQ^5?C{sV12vOyHSExq1Gw2gKB8vF*& znQ$1~W`ZY!WU;)9tcw)WH!K_@8jZ8k@8`ibsks#aWRbU`>Xfoa`hMB8i_U>lG%-T* znWjHF?PY>{Pc4HzYT2?!tsn~53kQxkMooU2g86`uUCKHZ3Qbp_u;Vge088`${OA<3rZEs-r4{mqbWGvVa*7g^*jJ02k%gIhLoI(aB|7 z>ZmPW@b2JUkq*Z#H=?F0ac_D~ciP4Xng9#?cV85Nz6GSyfiW^l2TqS*gVn@3E z;k!6QcnC^&p8f9I6Be!oFOu=Db1he8!r;n%bU?&TM>4WdHzQuyTue{KkWgXW1zK7Z zDTzhlKi62XG^Tf~(Op&{eE`M7CmX0-d&~c21=cyRIGOc?Aqm7OL~YW#thy*^dWvsx zPlxE~(+_2)$VLnZJE+B}(Z}I{JrB+nsjyLsi2e+|-G`P}hSI2D8U+Z3U=d;DP$C;0 z$(IV`$p;3DA+38yZesRxAX)6uN~5B_@)^uF`v&$|klh~JmwcrFVG+}FDS3}gvVj3I zUW8$vW90LeL5l7Pb^J@gsXH4-0Ci{gM~E1#Mp=K@;M@DtiqPX#5!yxlE$GBx;nnDn zxF-?rn#!9^D*LaU|9rQJX$I`KTL1ubKu;=jtTHPH41b|A(vAsvsPR?zkbTEPbetsC z^(v%dcDRs`?42@eLD+)IWlX){Zk29Y$cQr7LNxC%FE7+->XTs199D@`a6ok%Y8rDX zy0o+l+eT1!oe*tpV7497L2kdwOl-Lg!AO*3MrmkuT6Gm>6zII0PI~EkE}e9uX>mFp ze_+dfl*~);h0t3N>K|#j^RJ1kxbh={`-84wY&|*mC9k!xj)mWei!~kCJX-hdZ_tm1 z7BpLAYTYfCcaVCJXZV#Vri87bo#HIEiTqUDfZlBW%OrzRB_<1|m z0+33f5tVPalVSIq{52)@;5D@PF32eqQ^=i4;EfJ|9vqddyS92_#@x(*QDRD;*I5Ljfr$c7v!CElS z4hB#nD^|_EY30iMU=E7K9Xopibx$Tbw_$4v5;FM{J!dBKlRFn%^y5UmVxe9~eiK;m z3!r^)mo|%qoo2Rx8CPposf-jSlyK7`g^L-K;ncda%9tYl@ZlZ~l3K+E!`9gwg**`} z@*`R;)eH3_AK&PbFO0-{^n{+t#7>@sZH zpp&qDC=-S|4TJ_hsN@YXUBxI-#^1K_0xIKszXi$4)31L}e-6+soB8k`OY8%8f2p;C44Gk3@AR ziOz(uMJx*^nqrGGC7C-_ouvwB#vO5wjntW@ET4miAv}_~bD?CsRec@nn%m!*p-=jP#t^tr~wUr;NT_i58$euOy z6e@7`FtH(`r6BiPA!tK<(ryTm}d% zW}gC{4aMghu6)>JnC&tBxY~Jm3)evIENJ8wxc7704FGC z4VieziZ8uLsxW(li8=~mRnIcJ{wA?X;&B9Mqf$0mCz$z^!fv5zix5Lqw0E2$@-e|| z=Nyzwv;R_WG~%LD+O6-o5^YcH9t8e1t*roG*=w0K;rdTUB6xCU2j>aySxz|_-4>qM z9$4B#2K*qnyZ#j9oyYxvT4dhGj>t9Omo+MK0{oIoQ|wqy7<6lxTm?y2wz4iU%O3H49a2 z&SIqmwn!2}*rjtb2p~cCq%RPA9iVq?n2GCEA`pr~s(rF7%_UFBbHvV}oq;7+=ey58 z((TaRQlOfwn0#vkh{bEcy08$9YJh&a9Y8m=JYg>#Ee56YD9mrO3nY0>_rn z^Dp>q!Mr4f6h7IqQ+_nb11ht8f$#nHfWRDu4!EZX;)$0g$04%Slhgdm*z@Y}n4MyI zBnZ$_iVWREJ3Q}B z4FG`|W1A*rB(tCVeL=~-JmglSz{$k&*3+3Rob6~XL%=|!xmY?4hg+u##Z8f-lWI`& zao9moc#$-l`&743$&SfBYHkcn8>>7aa`?y1>j_y6mp4#jm0U?u@2gQRzq1Yp)FO*k z9b|)W_0R2eG3gLbwkF6@ptBNs$kn64zzmOU#`&PGmak2g_$IhXH?mguw+k7i`*de; z=n(`hhN~$GLk5YcP1FV;@g@K(JMcAZ*(`%H;e5b4AtZ!p)$vT$)T>@-ZOZ3jw4LZ- z#0;WEG2R-F?yZoQPQOLk*Oo;-$UgLAP~s!*exm{J{dkPbI0wk)2Udy~pOT}#iP3g! zR!~g}*D}Cenms2>YCDvZt>G~8rN1Gm7(MJGbwcfVEyDt)_w9g3L^=hSaJ(*ktuzx% zYG>%?l_prAKnlgR1);yeKb zg+EFYmZtq$5vMRaxiAD4$Lpo4SRtwIRdg8K?6P5*CM~4l)t(y`7$HwU|A;U2+_09G zqZt%yJvWRz8z(^EY=+1L&#PCghM>tatCh+*$3*PEFygh|MWJsEW*BImxo7p02Cb)= z47THbfI*pP5uAxNkz=Clo~_JNT}-&2#s@zsmzMP84B^=*`Ub$@2QqfRnrO0p+zWg{ zfH#InISOss@ng?uXF2xE+=&8e+!n37G54_qjQ{ELKw3IYMGE_DWYU~ZNT^Y@mLcMr zcp z?l3N@IGa?r$l9;%BJ!eSQ@$LY5?B^_7UWs8gqSUV?BKk{{ut?C1_ZR`Tp{XG^UuvE zoNuzBjr2T7ETC6-N)6^#M;9=z@b{XB5?D(qSb>G}l?84N+9h1X$*8*iz)`uxx2Fw8 z8Ynq~{#pX>iUvGmg=A6(P}s+FrVMbY86lj;(c!hp(liVtnNa1?*dz9Y!c`F33J@|jUE#TXZ6I2<&vvLpad!!l0!Edivc3H($5m#%Z!G8d)oQ4t zlz=rs1Ybtkx2wYs00yg*cR?GS=0Fwpij<}L@hQ=I9YlkzCMqg5t!SOfEypw_0aFz( zt!gi6o@?PRbq7?@)0)WV=Q{8kKwWaF44Yq-YK!5R~Dk@6$)hx#-_--(pz6K zljWk4272on%VZ}bXtBP#WjHkrz9YAYDVq`x&<*kF>TiN<=-M28U57=U4!PUonXKko zG3Xikv6Cl7+$on4xNV}d_2m_W39V>bN4!JfDr}{&bNblmJiDMoX0>f?Ed6!D#;I=4 z-Qe9R(%9?KvV(?smwg8dxA~znBj;1=EKP*C!o%&umXI@b&^bO;jWW8J^#XM-u z5IQInp*?oMdgVk-jDX6BY3aVrnu|7CF;;mMqZz;GnuEqK*eg~4eewF7cr zdKt3yV{amyphfP4M$ya_j`FA?Q!m@RD32gj-s~vDfT%UU%>S~K&LyPBGTSlGpl@`b z^JiPL!0My!*`b`4;e1_-H(T(aE#B&>D?bOQw(s1`Iu(}ZqY1hXP%aCpC+kVG0f7j~ zHW(+);M_gu;x19=9E&yD86p)4^0e*`g(8tg4RM;$GvGLeDnIjNITJi3 zQG2uFIeA2*ePVs@S)KZh-%5ES9qU=OSgiVCxq1VI$*n!89Z*pL9zTL|hn91A-~m$c zAqkrHYHhDvt+##m{CWQF;bo`PSff&i>(z3WYB54r2yMsa`r5fdk&;S@J%6)q?HoaE zieyIMV4*yu2!@;Z-6f|J!I*h`7sf3M31U)}hF5VECzxbHLGqhe3KT7VKi- zb%^BRJ~~2^%@*Yrf)^lZU1NFvDHj>ASHhKWL;ixqVH7iBU1Oz}S;gg=sX6xu=m~iD z40%Lq?NHSX_-)W|N>o9+-A=NuthFeNQYu&X**bT_%yU+rZ8G^fc6T=|pjI_YYR6g6 za@}&u`7o*Qf~d_eHw8VZ(mPPE2sqIl_( zD!DGMT|B?0l~|I!H;P59GaYSK4GHup^&hGv6?g0`Y8Ba4;M2q&fO3O+M~nw);Cc!}GQG4et%Z2Jj2yK1wDUqh za58a6n|Y&DE9{m$#r-XE##mf(<*OeCgA=;^Z0hBmM@TM_is=axokdb);(VHfVBBK| z=*cocd6GTVO{KDmrjRYEkSYq9*g3mynky=r9`x<&!RyVa8U3m;hNz48?sK^$Lhr0r@e7PeS(}o0vl=M$%^`uUxQ{@&2Bh)9V zRgkF+P>3p%*<{+ueUNTejhRn{Y2hF!)w31K%LVEQWnnVK%T2H{vtG9Un$$H4Wf-xx z)oP>+~5sVx^;n1{k6TA17*ddY#D=HMKBbYK8&4)d`!QaB?}Z z3s}kWQ)t!oI#s3${#!jaC-$tsdB`^+cWq(EW7Ui@pE*rs1*sVv@UqJR_vT|u9JGk- zb^SIaPu)|CXzy0^(3CE!n7*gEnL-p02neRofYeD2jwJJi6MMmoQ=MOO08qAn2J9^P z1~9Fvj==rM@=y-IiO3Y5HPB23=@J;fd_4h;O1;42gKf+j4a=o_0Av%fa4cd?+6asaClzx&{F>pr?Q~Fgxz0b&TBu zVR-2{sV!g*ZkGy>Ev0)K`%+VV+~n@-Ck>Y{Kd@j#A@C5z?0p@Ija{Ha(cW>2CJvh zGt#ElM^j!VJ#YUv4Rw^@^UrOReYXOJPy@Hh0^STk@zUhxr`A?ua;L#}W9XigD*g#c z!>cXI=7bEovn9aPCNevawj`4}iO!a&b;eVL2IdijO0fi!aN;$Mb(F`BA9+UEcfgT{ zG!1eW!ta}@#7*gBs`?oFi^0O=dY(%VXu*9`*i7J{s=n(zTSK-2xMvG%Dl=m7G({2_-hxC4M(g7{%)63%d-l5q-lVYs4+h7lC8G`YYo!`0dVo-^yy40LplOQ z#^*|zR|)#egmi){4NXx+2q^$LCLEDIV4z)ADap!+djvTkKWA3h6^QGT!)a{+3 zWAPdkH7iOxo~6sJRGr|aJFJP-ar0Q7S_AngcjX{h&1Ni~sEeV}L|}nnL`fJAeMXV| zLLXC4ax`mgA*zpMV{Gy_7QaY4v6coUd>Cr`CfpB#cNkB0#zCFy005^Wt4MZ08_>5% z9u#W;rMsf$R`Uw001X{hz`FEx}# zD|k*^|K2~}ht4ob-T)(FDTF5qUvdp5(fAZ5AZB!A)T?Dm-m!PIQ|JdOYb>?5A5*R; zu$AE!Q?6tw33#6DcxD_H5Fmj|Mk$uBnRg^nHT+@O)j!T*e}oJQRq9s~!^(O7hXVi~aRv5b#7@X_8~#wH3vv~Sl~ z-mM$XQShkNGRqqc_zJ~iv_TQ{MsaaeshxN&(MW}M%>eK)aNADg{!}2+7xtbYh){mS zDUPVS#)HT$k0U&hWl*|TjYv}PEivsPccJFgDeeWb>j7B{#gQXB4#@L4ik>@Qrq>X3 zCzyUVSpEtU1y&#lIg$}?cJl|UnqCzwmdiQdJ7rt!!CEg?VniH*kn-uo;V{^*sH7AQ048B(V%qy6)SHiiw4Ly-! znGda??BEqXbo~~I&@I(j8qI(t9Kd@rJ+NGI&JMN|riVfBkpG6)6Y~gwFC|XTGEvi3 zt=LCM(+4F7auHMTK;Ob0(KTFHbiahhc~ZWVVgYOB+4BB=qf|fz1>`Yd>3+lt)XP~W zZE~jB)yj9M&&SGAb_r6+XzhuV6RNTW8vP66nVUykQ>dertBf3!+B5*|`e37Z5_ynb zj2=2?N|oZ_jk!7S(f6SAWn(+6f*}~g!8K5$uzwGz&fUwEJqItMq~ zmv33PJwfU0)^I!PV;>5ko3FSFtx>XII2Bxz^@#gbbo(RI+48dtV8{RLGa2640*+|% z-ik}dGk#c2SQHO|jhT9--UukHPpDhA&0P&1o2Inq;%9FvUDhzrTtl}$nrcR!-WqGh z1b3n%!o@(yjI!@$P>!hgX4v!M5gM9^dUh(E;90#C+!Qvol6`1a?8RwaFILeDC9t+5H4JLXs9KedugxD zTzlmM$6sS34n8c#h!hu7m5rxo3_sYhe&kC?d8Ih-RzD6BPgtrQiknmlamdV_tnn#WpJGdx5=9H zvkAzj$$b*~pV^Ls3~w+L_NE}_LW=-$6*WMS=rFLXD@TKVT&IBOY$;$0MwnJDTP;+` zw(b^pU^PYClu^J6t+|azM`qQk+?}fhsp8z`ROg`nnq+=gWb&52D}DMI@xTbR!G4@! z5D=j@*vS@@&u1|P*vh+GG8e|#1NmlXrRxju(b%-EUjvVS&NSC9_`Ee_{SHzNRe|@Y zS6X$;bk?Y%wOCH^6D?`c=^{GM=K&%6TSxquR|&-JS%A(Rfw|t4C)73l^|3uc{`6or zMiJGs<*Ib+BZ)#})#t<g?If;eh^Fq#f%q=T$)D5_$cJJyCRQR!9U@ z!?NW@scu#3RdbVjf2*%wjXnlIY>J2~5m&O{UGTx2I6 znN7{+6A33DO(&e4SUQ^6iDYM@ivWq#Jt->qZqG_dorgFP$DT=~QnT57EFX{MY-cu~ zOhl4)EWb08&f3X|nMiym8I31q?PkH08wNIZPq%BxbVt#Lg^k#PYV2NKec}a*^na6LFm6Od@Lot5Vr$G@YGI zL)`*CMGn@p!slrB0OPtVSzQxPX+ zM{_&*1T5u*U`4d3eP2CA_Y=SK6hpE$&{+~!yy{Y##5;@)I&HKV%a=2 zln#`~5VF(xcz!3aq7p(`bYtL?*gi;_? zOFgE>1i2@DE6v(;p~hyR^eva%$?=DrwtJG8c=d;?1JsbS1}T zC8wv{mcT})wC+teJqp^WZ#lR?T9X`IQ8CXB&vddA06tG~C}-iim0nsFyUqzHLZoZ4)haPa~+AM&7jstdQdy>gL~ zc?S4Np(WwnxL<~u)D^m1!x7I!9+YZm%em=f8zk{jDi(E8x>Kv$W>6zke*g)+1D)=8 z(y^-JADQbFcbV*weB0_Utb__iG$W(VIuR(5$GQ4KaLb-=90QS!wK9J+LH z+b~a4@)*n@SgLkgNC{C=*Au!fsbm4GkJz!l_W?L`p1GOQC(_c|L z9J?yJhsq^oi|$$o+@osIoABL)gDj=AMinpqfV=NmUZJ)RYG@vWTS528Nf20qy5wv) zRg3ZlBQveYU{nPLPLwlsr3kXw=ZG__fC!STtRZVJt7OgGYX{r%Ln0j|a{tnZN3#!E zwoU{aIn@IVMx9nYpi&aTakZ(9u2O!>gTmk1ls6e0-XwU5=y=QLAm`UDJ7-rYi*>-k ztvEO{Jjv}-;UCo(WHRiEy$=`Gesc{9auQYG1h*zqlL7h~co{UUEqQpwWTP$S3l?w*7OV9}> zT*gl}7_hK1`|$t*c?1|c0jTPM507-jW1xCbr3gf4pJ}%cw-~Ueg6i}eUBx-?7k!MVpIEjsCvO;8a8=yO~%0tGr1<`%Lw>foDrb) z2IA5U8Q2@h{_8S|uYOtQeF#Cr&So7JKo%rv=v}FVuZw=_)$Y&u&YLG5mladuJoBLW zVGjaHBx3Cr0y5F>aEchW#w0&aBykGz@Ucd*w_jR1iA{yLbOrV8p!O*`igq?gro%*C z9?`~6>Ou*6geV=_)-wBSZd#j&p{iyb;w0daLZ(|al#z%of?%ulfn*E@JwV@T9GYYw zjt{q)DpQY^3n~3ZWlpqI&^*{r2ARF8X==j*B(zu%z-HLLX;yv(OS-;io<`g#4q3L8 zEr!~~(c;nX_L#*$d7PLvxy!f=ok!;jxE93f1l`l==B=@U5WkOQC+bTGougb))I4mv zC68}KT?OjE9d(*%{ZI*F)1q_1^dy`tkYM5oaAg|J&uq2gUjHfK&;WY3dlYyCkU1g96IK^{;lUh@NfK>g9cl zc?DStXFlOf(-Wn;jwkQSW1hr#s>EiMD}jX2J1$_}Y^I^InZS2hjiOEIVl6wu z-&}!JeiIYq4L1#!t7yZNAs9+48aZVW#+^wp$!WhECU&z>V`2Y=QiJKk$lW7SwPw{xyPhl%xr}z`h#d!(7l1sGe)(^KFv5tsZIj(1`jEV8(IUILyfW2 ztczRw4Q|%tx%X=p^rdlgec5-PZf-F-E)P#UyKYh1xw=)CGo_!QA@Os;ZQ07HECw== zFYK~4X<2iY87D&|X^-G}dT%mC1!`@~ct|T9lEfKug6fnm21@e4l zQ!r!`EfgshxB$|{d^bSUtOI5s=W6o4=$cL(LETW)Hcv##3ksC=;(H}ttz_b1pYA?A zMlVO{80(8VBt?P>4T2y|*HTx`w$$8qcise?6~iSj-LoOBR)4D98h7m$h}FvKxT#)kvh`}sn9y4L5BJB>tRjoIAl@$rc9Aj}S=U&|qeYR9uzB<6MRhxs2yov5@o!R-)vV26y_ARJAHw(ACqywu9Y1Gwx-mO0QpkRnY7WjzYugM^gOJg9cY zO=cXcV2jSsvj&bunZN+U3TOMeS%@b|N3Q|ov)hhGi$RTw(Pa}0) zRC?SHak50&la0C^szwBY55nL&G@?6Lw3=$v$6ce|Eb5I+{_JkosLptS%zN`p+6k@U z9=Su@UQ@TDwr)og&SQ;-UtNeYW-J)0sTVN?P`7cun@kPTL`xu(r$CzSqz-n{V0qdJ zkn+dl1Y+5hJ7n?(;)Ocp+9oUNWqQJmjo0wxZc|pICs(Ld?0R-jRNC>JU8OKP^*#lj`Z)Wp=)fO^-nj2HhLDHGRVN&tQ4EseW?Y^^;wD{C0Yhg(?4n6eK|#G3eev zHYa7Ab{AmM~W^+l^kp}B9T84IQv3)&9)Km2JY5Lm;5u$u{2lTi*m5#z;jFR})pc)|#8`+*K zRi|QCQTCz`E+D86I?xqswYjOINJKJPx!7oCSAV1=@28ps>OD7HuNKnPWKA< z8I((ts$Y_!WG?w3W{jUqQDUMQuTc4!w$F6Gd6|F?SS5uPCnbR8dER$cK|$G7$FkpU zIUaI6PP48fLZ*AMRw$d(y{JQBdRk7J-i?KK4QVs;V!Bq%DUwfNp#-CSwNb&pLFQ9v zv#l%JuQHb4>FU4%4)*_i4)4}2J7>3e3904PcbLyWlcTL_uUSJ1Pxl>b4~2l8YRz0a zy?3-zh{nxLdbVX1*-EvXb!xS8RhQirb$sD<4jSZsZLalytDdb;l0bDV{_oTVj2~=5 zY9qY1r?~<_IlE_9EtnY#^_nS)Z)(T<3hKG=T>GrC6|FVFF|7FBVANlEDd?Rw?VUA| z&Y!)>I?61o19xXFv}nG0tMjbeOR}0T_k#=yo{myAoPd6H^?9c(bMeZja&Pc7fU=&+=r;+^LpY$(xbVm$X4&QDdSk?vSJxg|Ug7*vqlCs>_GrMl zRly&vG5(14LIzr7Z{1_=Nv^fFLspBiWc*#p@Th3NDLFBde@{Ztz;qe+ z6X0pw{iL>76mAUx?72n}v#U0c7q@DPaJ;H}W`fywg5cCej|%InLRwwZ_A?|7`6A4O zOQ*{f3KncQW{UtswI62AV4Odev_mAJv7xiT$V`%A8YO`aX8Vz6Jd7$8O1t1<@h)nH z3LM1Fv#}AwfV3>7N32ii+j6yn7V05tA+!8YW{da48S5xB*(2G@)P+xWw5%fgHCengD|P`%Z~;4!0;R z0G;NFIwFa;060drdvw4S8Ko}X(s*`K&m5e#Yu6JA=GZNcAM50=_b4~gt%NzH#{Ld? z!dv-3I;qPonN#b;ukn;P^e5V>qKqYk&M8GWx#H@{U*jo77!*Z-JA_rpnbHt^kn)>> zq5FD(9W{<~qnyu!l~9#e@W}7d^WZY6i?DwAO1WlkT9*9qAq-Ct3 zFZy|yeQm{Krs#}ZP)y;FQm)jbic3|1KAgF5VLh|Sy-rLs62TWLB21bQN~#s6+)p25Y10I992QiczQp$j}<)0QI2C0!6_GG!ZhDwfdVH4c0)v4cdt(*dWDeeiJjCi7n&O)c(9-D zPruyR z3G;dad<3kqmL*9G+f^X^xyjm1i9e?TK-kqMkj}keZ#8MtEM>Lhp|jL`=`v;o1wK2 z56YZfw;B4-!1`cYT(oG*vaYQ!Z(7%{At}Q-)Xcr057e#9Dci#qDAydz&fWmy?Tx(y zuV)SNFjasrRT*O1ALsjmyl;qngN)lCtt(M?oO|_p1;ydvWC6$8J_pLYnpucvsa!AQ z3t65g+FKzXb|KmQ74<Xfoa-tH$Nw`muh1E**f%e%xx(I1z(2nLP^(h%UgY8b&v zoj1%5Q<=EApCf?a0BmrbG0Mk*s6gTfFnWw^HCPCIxt1 zOwZSYn?sZo$bDyVqj1OeBk))|Q*b2ZEE1b?$=n@~PGj4(%pxm?Of>qCI5aZKc==;R zHWnURvWsBbOV$qL@f+~hY-_OqE5+Q#T8e#2?CtSxsD1mUwNpkMO^t$Ek?#pE&1RHf zuVWS+2QG_9FR|tE-t70J)!LS74>qP@w(P|X2Pro+_66iJG{)E)JXhz8c1b}>Mx<0C z-I&hnK*6$C&LQ@Ki`)W#g3)JCgh8n5hRgZ{>IUONsfPHw3cT)*l{mR07!s3Nv5$&n zd)OT2pm3r>p7cXo59><6agAHOX$qQm5p*d-9DNY4G3db$T*hG_5v>kyVTTPGkwJ0D z9G54i3DtC=RFI5p)$E0j>{#|?Dk^syIQG6M4iczv^D387Atu$&!+)%hb4r7>4$9!$ zpU~QQO;eCWoHqyF9abK8HqjR3?7rv;;xt!iwmiXt+M}rqu&qH6L}WIx;LAgNrh*GP zKwTkXAW}`oIOAVzHqR&*{R5bp|!L;AWBY0db!epqaE0_&r>D; zJh@36emphN0}q53Trfrscb+);2`7?5boWM*gD$iP$6#ijag#=nn`;)yyh>$uUwQ*? z*6J+T3`{$4)8G$q=;L`-&kcDB->xRX8QU!S5(&w7^0Rr@}5N>)|;^GH(u+?lQg? zks^wna5)QBWMl~l7&o;5tg0bI0Q8#g@xT^PcFeiSd^4EL$2*jPfW4*phHC{pAlQZNH1RU0u=C@>$)HOw%}*JK5Z~!^_{#t_DLFl~uYuO7~Kp)2iqin%qOWddz z5Si}tzBs89m#8&i?l@zQ&=U$*L7R?e6HX}1Bra|bkv>i+XpR}3bnRA?SQ~WC^ASV0 zu$EK3VR2*KruYR)^K6e@EX?k!H967^~IY)|N=&+$M9aT2H4_$lKMbePphj zj>P#JK6canInny(bnBnbW+YP^);?7)<0=cC-{owr>z*H^um}j;Ryk(g!h~@zJ}|k= zJ?wiFd9LxAG9R5P9JhWS0UCX@(SsT)Xia&vm~~Ka4#Tusff$h^PZxrKswW?!TC`c3 z8*$&D|ABx27>)%Hx8wqZtU(90BXrzvnU^$(0E!n{RhkIw0WnMy$`WeCZE~l<_iW(K z6>AFikZFb7h3YndR+1*lwcBSSlcoZ4iK2!rQ-}frofpH=lxi+S-48w{LSn6mPj{{C zx^Z4#kJit2utiXcr~>sdDV~>3GiMADpvNXUHfADy))O-5_TYDk%7n4c+Hy?lB)V80 z*CfezN+(iMFo=3+sEc9tdcJvv6n8CqYN`!oL*uu)Od)8z4-%^(vEP0iQlS_2PP;XL z;SfHP0Xb0g&O#q+kB-d4$U`ak?lL}#|EryOp`B@AwZ9X4o@!~Nug0-I@M)ejV)9_}ITy;eb4!dn{6xU&Rd*)0K(Q6d`$ZP9Tm zRs*M2(bUW3AR*7gnQdeTx=FGGa&J=L0QZG!=i9S(;X=zqLX{%Q1QJ@qO~%dD*dw); zg1vW2Ny)aK2EC&f-Zr!-moQZFt)d_Wk&lA(2>iaReZn59QAJI7>Y>UFTaUuRjp$4TSoOVSm`#cO;iT0W|75#jb=Tq z_v%Wx1BuSyFjrY5sh>MOFMsB)qniN9tfablvg(Fp%=F#Y>)O2vXroUJXyxXa8!R9> zy38=7Nz=H1kJkDgt63}6v=|N?<;pu$^A!aOoos`Zesw>owvr&;Xe*vbBB&vNle(56 zM>X^~MWQmIn^^i{fa4@05iq_RG;*t=^+0x98DhY-4}*x#P3?Nzk5wj_3mN|QXW2T2d z123F{4ixR&8Pw@}jHo$a61l?|v*?sbLv)*4b$(E2VvZe?rt+COKN&>DWF=$C%^?tF zD@noM0S`+qZHiD;b7*vDk`SsLbH)*cZMEa%>?fCv4wS+|H~3o6rrqj&hff!dCB@Uj zs`UAS9kC&4<>B+d*~coO1)z9PXQ!E|S_TrymX*iJn)p$j>noi-KV z#$A9jJ68~JQB>2=YMgTqvLHJql7VdWFi+$uT29fDDdj`7XA&)^R8XnG1GcS+C7Ehi zVaDxK!y3v?HPch*e^IZn7M#{X`uf^u+Gljw78M4Xgntz!$fqDsWgu}Sq*2ww&+nHf z9V*D(j(o?E<3Etg_Dt$z0_@T+gMhJ_6Gg{mNBGOLl}WP%Z@3kIGF4xkIQ4~Gd=1aL z^#I#SvhCKwCOt)S)T*00rm zU=TSsO(lJ%Z<|kvJvsQZbob#objQ{Qt_uyI@a8MjSX$XaslhYj7V=iLQ7Tb(8$rCu zww^`0Ul z3!|hu(=FAHobhkZ_nuH>yrsxs_YB08a0-(t2`8vy-m?|8b5}w$9zr-bJL8aNaO91M zLmpomJH>Pvt}@Q{lR;qmun0KIR$%3vVgVH#>!<*<3)a>;V6jkY`q$pdJGxqO&LSiI zOn44h&ce1*hX}PfRp1&*ylQ*yV4}@5m& zGc9+xtwN1z&2Gw}*0)xN`qVe)*6_{gC0@MhOo?s*p&3IF-1XE?Lp|%kOX$6wlrX5F zT494!4^yH$weJUKQS|EUhNQG>M@*YYE!2;_dtnWao&>RsCx@Oik;>r#($gMfw!(6A zcE}_SkyH(D{!>J%vW&tM^*JCDW;h}RS{;YM6HdukmX~DTqk=oai&%4?brb8}I?2YS zWQJA$VIynoEBf*)h-DLiK9VLx0J)AUT;B8HV}yi>#BWIL-3!Y!gxZY?;% zeCjbfXs)RP8%U_**C_a^Dd8GHv5EG6G*iMgC0yky0-=;uz-INy+AeqwXNzR&dj5yu5uI{jk_=v%! ze_1+xfRvNvs<{QN3NpGNL>3udv^1!ImRFEF{4wDlA!~SBTdZA5Hs!CgEq;-ewy&v0 zwmWOzOsd&viba1Sn!z4Y6QFMzi;_*R!5soN3tMIhgcURz%QK+X>+)T z!gx=;-D-9}`c~F};VE>cL6;*8y6kHc4!UH*6hoT>?{t&JG%&Md1y}YU>Nntm1cOD# zshi8x%_Y>$^BQ3GBcF>3P;_p-sJw(LLczEuG z#hJ%k(rxNtnnN+SHXf$aZ81LEVtO=8y{uc+_83@(h7;taIF$y#uucOL%#d<8xB8Zu z9x0P@q)ZvHkuze$a*vvxNfUA=P50bH?zw&sHOsD6?IUyP^dq%$$*S2mtz3EE$|5*) z$IjlcDt5Jo&`~Iwd14QG%G}zwE8HV=O9Yy-C@}s+jTL+~SCAVhyJuG|Fe8O}&0G;L zsBxz&2w%hh))nib^}v-aFCaB$ZrZt`g`Nv`vG6(!pj0gH?c1f2Nkb8xuHvKj&Z_WH zMU&pc(k0Hj^H65hx`LcL#!`(6K?G0##Lrqduu;hIVF6wx|Zu4zOToKi&{{ zSM-{Pdr`%A8Sq#E02=SHVa`7@PNe@-oCH~9IQB1RGF@2tAz@p z-L0Hmw>kO&-NgHZ;e=V%m6(TIc(3Br8+H-+H7pMe8sxT1WvWirDIBm6fB!xh$bMVT zY+Bc^LGyeLo9%*iMcr>@OqcCXJxu{1Tq_s(nA~(toeYKn*L_vrwl#orXhfx#N!Ir$ zqdX&W;$r|pWw2J56&Z^;En+lGIV29t)f<+0zbja|7|>vZmMEfCC>4;BA%waF+> z%MNaHD*}?7ZB(mHDSM<>+1<2@&Vf@ji{)K9vgyyEqg0xDXxTedX$xofoLr-bV>KNZ}*OcBVVjVt#wbdteMPKEsGtHvv^g9aw%7!vkHwISx(8`DdMO7 z20B)D2a45?mPAzLB3Nb4KH*=--tU}=V&OShg{6$ksddkXab)$z!dpP{kZ#98a9{3Y zAROffFp!}Dq_?#h4orF*%YeYrs1};YKpzOma9H4Ap|pF8@qQpwQpW0b=Ze@oqUzNE zz@1E{ad7YgBUCBcka%z=n2ma&Sg8AszlSsZqV*OXyUX-%R1mj9#VQ`wOtCn7^*R^? z^+@f%PxP07j}7BNtMzgo8>)y9ipdq`u|eS516~%qexX!pKzPsD72Gk`mscd)0QN*M z9iGg8mYAw5cxWA*(aJ3W5x~m2#>hdC&R_fzn5;`EYJ@h0(-r~I4q@BaF>{5Ze8tSw z%Qn*yG%v|DY^hDgW;BazdQl}i^NxcopSV+xq7rNLF+BGZX8d%EeBq7 zm7V2>P!;+kRv9?d6j@gt_#2j>ZzA<#wUVt=;H<-%#QfKC>TTRux2yKPL;IdIM*KOF zDsUuV)fpNE^}3BY96C01n!vDa0qg!=*AF3Z>_Ul#M0VMVS*V1>ui+Tbe(7@(m=}}a zAQ7_jzvJyf!0HQp=viIHrPKRPt!D26U4TZ`er?x+&KBR1d$0)k%M?G$goN6G*ZRk< zhH72uZ>a;{9~P7q0B%;Q<(o%bj|J^{d03_3TCBM;8ad&96axe732MPsDv0iSlFBbI zLv44eZyR^n5vhEjv$3;Thm@2Z^rs#j-FC**20aoXes~7a(Ez0An%3|GqPsp0VL2iV z4Lh+%TA;JjaSn~%bvgi0j4Ws_5Nnh02E|LmOOtrd>V&t`z61jx>4Lk5phv4$j{-5f z=?qjYcKtNOH-`FeAP1mg?rNz{l3f#v(e_1;5L;k7AalUXAlQ?(@8U>qQ6#>@?JYFV zY@IrMrAar_;d`X%HWg)T_ryqWR)LUl*)J@;pF_Z{q3xy)xR3Bn;0@YC1XD0vjJR`N zF2w1s1>D_+LqTTkIgEWK0ibHVw&64!liAg&cJ;{Rm>}7PbnHUTVx=?SIZ$_Y%OD2~ zpbuxjuZt34@`or)5ztlnALo@fcFNFzn`Yp9v7i@W0LI%?nI+&$xsZU z<$eJsBH*xHZ`4dACV_0K9hwhktSjdUUBt6wp3^w?wx_Yy9HW-*PGJ=+dBQcN1J~#U zF%~`>{7~SAMCM!(Zr_x_VRj(y&l`a_C zDaAmZI~l7W-rU&B;L6t$WG=L;i)$CpuesVc(v5XB(x%ttI6*!3N%_^cdHrlgQ8bZ- zxP@()PZ!S%-9N2l=L2aYWcM|YvPBBUnn^BUI8&{IlwUfKNWonaS5HTl<*G1{Lb_oN zg`|(9UDM=TcrCM%S!PFRy$sgr=CG_eAX=otBN{W*e%6;)`MF-k;Z&FBVBo|rWCYPG zTg*js*{)+FE3(d}X7pvxOpY6;SbjF@RjSr@B4?&6x=+l4T`AWK`2yqu9Kt7Z-8_55 zN!)LkUCm*eZq;RvPU1YTI*_j+d!FFAo%984HX@z`BFcpZmwP=nd4ZX+gCrPX#&5-9 zw=!`HK1vub1$P+68un)BWI7hXMCkok2Z_;j70U1qY*wx}yYSfX*{&pmJ+S_SlCKZ< zs3_OEfsqyqGl(W7*W4hN30K|-TIn@`6+nZQn5Q(tov9h`4-uxbw^GczSoB3^Elb(Y z?7Ik)3in6Rba8#Ulq8ZVDZao5jjJ|7tV0iH&PxTZu9hm_aCpRzRf0DL1TyAaCiGEK z8Ok{J$&oXplFIXAxt4)8>3X8>&Xxj91nfw*r$9MiE<)vC|6;KQt^C;KBcy8NkYeEz z0bcUxHxv9cN1_U;>D11#dthkd-d5tX)Z)sJE;&0@4Y(R~GXhdxhlEa!GUUm@-13Ll z9orkEErj2F`&o$)m7sACa5CNY(qaySM2ikqW{}-^v#KND1ej23>^nJHBlZ!_#=@1S z*1FWFv>elG6I6~g2*+@QiZde=Bv7U9!f8QNUEM--33FoL-)mM>qvY-T*e_(Ous;BH zxv|wCmkAjAg<`QV<^u(htf3Yz^YvEgATtRv6R!Bxt3C^#LPm4lSL;^Mu^}n8cwaoC zHNu$^YNZHMD14W5@YcgQz>isH2!l`dhs#0?A-GV{vKLUcFp`|9lDd$6{fp$yoM)^#FVg6BmfgbNwyn zK_`qsU<~y21RSRu@Jf;Z6bM4|3h*YwI~N!Nk&#jGN|QQyFhlNCz-5@tJ~g_;EqMBl zJyKmktx?K3)qJshh{HyeR=STyyPVax|0qD(67-1K6n7VQ7Rw8 z+j(SuB8^fPJ*xg84ahYRRN%C&gv2u60UiMxAcsg2r=A<+tiVx;HD_I>@L4X-u>I@% zotz0m+Yfk_oGuWX-2Roygt$l15#6Z3Z4l8FHmvMLoc}Cc2A3BLJUo5iIXIMV$p~C- z)V*9EiZf8aqPwV#6d&1g13DCJs`Yx+74E@{YTo_WbjRANQ$f*vV=1z_qLrY5C*1Fcc}XOQ=XFa{>m?(4vMua+ONx zIHK|683%k~%J2k(lneToC2|9F|9FUd^2n)tB%)Cp2D2WIC7V;pivh(?x>J0 z0SaFvFc|W{f`_%~?|#O&iljqq}Y^dy-HPrVA+NO5hNi0N=YG;Bt9QTS=jW4HKykYtKfK546U;?~bkfu*R< zS5f_y>sy4kXz*jg@tmJ?+9NgScqrcw$evx>Lri4FT-(IIl$J(lL%eiV!p(n80$b(0 zgYFTtMncb}fMj#uB!%K5KNhVi&5t4uu`(e6rfVoS%Rz1$@@X;dW@V zW=6+xnf4YeYOL@>KOp5sIn6u>HrvSpgyS0lf)noIMdBj$af1z?+;J<=hVjhR^Ql`A z8(d6#&#s$SE7>sT8lf^fq{nvb9Mk)f`)ig|$Ugf#VCjEc8u2u3bHHv;u8tq-3^n#pa3iS&Zd ziDyAs!_uTZK++h??UqMUwi=R_wN5A|l@8Y8~Bm`)(C%1*icN z6Z(nEropTYLUd$xWd=4PAU|8bj;GniU;P-S*~y61X)>R7q$go=C3G~D)`n}%-Rw3i zeM>6C$fY*)Mbp@_1tO~vZO{4Nev(~CAV$p!L^`VK&{#!6(M+T)4HeyFCog0l%nfv zR%Qn~n?9VSU`G0Ij-7e*>2htC{ybQ5O7z$DXRpz>YoxA&*4leq-nmAH7<)v1xQ^&? zV`D3mS*hBGaHyuwx!iK24&$)*C$2%g=~Ue(Htd}m|6vRF%lv)i zhW9FEl{r@^I^tFO`hau9UZ&6MYs+M!pjX+~YB`UG@s-_PE;g$2^ECujAz}t!&q80d z>+n9$742PWOucsnN;%&x-aCItoeIArX2alq4k>0y2TcvoI$SElhl?g4Z-}PoHw+1? zNBLv9SgtM?q3)w6-7lA+SY=*sp)z4Gh1JLV;m9M#hH^S7Hpc}%E~g* z-q8mK8GEP?{$ZuuK>iK)>srZsW&`R!`hl+|ANOjhvF~4Ay2qB$UA0vbhG|~cxI!MS zuv_cZG9~@?-Q9!`soc8hWMOW?E9a^XYlQbq-@Mg6@6`DMuI(fH5FU1KaG>%#?8mhg z{)MfC_iNSOIa@AQa|E38__9IqoA`X8k-Go@F__@9Y#$#lA!!zW;Hr}Y`t#4%Dn<9c zfY=(GKKP*5;0*=;K?Nx;mF*mU!_H!J#Gr2Y;dvSA`Q<7|C_13e;&(X`x^?yZa%R17 z(<#y;!3q&wXQmO=&$#5q`vCxV{!RC4tM;!QCkQ@#cpEcVx`zuZK>EDZLNSc8)ZT%!=fzHEH!ry!e9mTyPiin zbfWk*!#b#y#H}KIaX-Vb2OmEf(4VKn)?h*~Le|lY$mC6C6r|KPWFv-Xz;K&24)>k? zY~{$ne}8@F2K{0Gz5ZJ^jJ+2>-+22e@7|p+z~{T3Z(Mq=ckhEQ+b}+kdw=je@7}%N zyac>*%2L1P^_ih*; z!}W`}-hu1AfY+0_eizqgaeWEb-{N}Nw{93|T`P2XK7|*QaoO7T1?>eFK;A zd_0fqhj7i~x`1m7R}NPR*PC(u46gU$`ZZj?i|gOx`WmiZ#aRCU*Pr3~Yg~6>OfSck z!gUeXt8v}L_0#x`{`;R^h%w;$Dz0yR5nzPtQC#oE^)Xyu!gcSrZ5Tg_>wm%Z=eSPa zwPCyp*Co93CH#H)w{I95xITvK8@N*6v0*%h>nU8%ees5I4%gdoeF|6NJ23}bAH?-F zT=Os4Fb;728LpSzjqkWF;(9f%IZ0{brDwt*WI}G@Z5Vaclz&dfahPs^;KM7#PwNR{~Fi-hU;-$ugA56>msf+ zt{=vAH}(PjcNaeY1^WFWuFvB76t0iqDjUY56(HC%V&dI_%Y!1ZmoUWDs~xSoUe zzXgBah3hu@j_Y<@cj9^;uIJ)9h3i{!-GPhx{1E#7d0ZdE^~<e_zCJ^xr%1wz>keG>-)+XyQiP%wP;e;6 zqydwEGo_s+vr6*PbMRcWgXeC?b649uN50#fw(KgdQCvHSPy_uq`i?vGkCu^-75 zi-z$#ry%d+tUD_U23)plH;l&&#a_L;T zdW|`_Kgde-05z`_9j5|b@3+N#+2hv@Pj1>WJcx%`u zTX(Rz(+QhdKWi92d1`~FWBIvL8^szE=nUh%^cle1BJtOFAALio{M-LZ-$*bp z8U$~!0jNpA*Dhlize@LT`ez}bQa%d9`1I|ZwWT+nxDE5%x9gYj3q%M!|9<@r=J!o8 zUu}wb@^=Y$3i%`ai}Slg{66mf?WyY!W+9?gH_B|%%gBadJg|E8%Ic-Kz`A|f5f*X% zg}SgKVhfOu_b<^NX!-@nY`pvwd^)psV|NcZuv|SqQ@N0T5FYs_&snmzndyOqVBRiL z@8Fkr&@T`w!Lvy>bkc0$N3Np$Df%6H1FM|3uwRhaRkSO9f5R#3@(yx~A7%~XleaBd zfN1uhZqhi(wOvesoLx4pvj=sAfv^Ni#+%XZb$U8yA3-G3{5rMI>rG@Bq@0!P(z1UP;(2 zT~%r#0ixJ5;euD75%Xg8WjJ^BCJCKtn7T3@^(0qk1z7Q5Eq~L+t&ZLnmJQ>%pmZrY+D_ku zgH?ox040&a2mUXDaj@2Zi+S|e9woms{;v1;867ctpAT7%4Ev5HZ1k>S2@rs$(^fb3 zZePk5xjlkodlyjU=f}%btb-2@w5y?oo7}UhC#?H173(_cNn+g};LW7On~vTD#z}Nx zEA?;tX@RZPZ`@AB%SapQu2&Z$0jxn3Ib|&ZYpj)Sr$hYCi?y}tbg8>tEW)ctwYBP~ zC$U&>2ZdnVn)G#G#aQL(O0)RLDGQu%j>66%O#HzqYn%L4EI=2j1xZb9yz`U=Cc1h= zWz6#2g&)lK|E%4GLh-d^*f}yjEZTbn6($VhN!dDa^~Q(9w?Yp57`VOqO}KRx>frj> zXH;LTSIf{iy~XMm{-VN{9N+sa; zAH?sMFkBaNJLG1tq_kkh8}UBZeD>PHv;I@L!x13*jcnKM;Y_&sVa6d4D<=0)=Z)dC zRJP*h(9&M{5GJFeuGt)njM*l?i8hY9=aNKN-*-d$@rMb50Jr5c#p}k|Q%g5%*f}1P z(m5=^>y;w~Pf$PYoDfk4K|LEPnB+7TEW`-buJ zcoz(PPw!s7wsm$rnuu`i$Qc90M%T*OIy5C0=<`)4dth9qPh>qXuCq_rFp&SD7N&1P z%HAknV_Uc8DsdT**@O zLW2H2amST?LGMTh{)(pil9Zg5ZBRfi3&BQ|2cl6NXr!#OH*YdJ;kxrdzlY~>`y7R1 zaP>hoZW!N%yH__>ju6pX$esnwF;)!HzwbfIagKeWQJkkwWTpX}@Clr`VI&P9sI$-r z==2j9G9D@!KhQKXF=h*a^>}mZ?A6UH8*7(W-IbU#Fay~B3ppy)jR68MRtbb0f#sz# zY}$<~X!^U1I!wlgI0FDbpg;+#$z6jmoolXsiawXfbnsGxz+8qrziz-TQ80{G({J@M z=zo@f!YbtWr$*6mA)~v$mhWv;%H*7wZoV*ZWs}RrP56$jF%YJjbfZe5)8E>Niy(t+QX`2~YFnex9fhtiJTU7z@=X zud_q!rTE5AGgD$v)G*S#PXeJa>wUHy!>$zW->=;dAH)06%KcU7`2e^3i_!a|_w$YH zdjg<^W>0;<-v;HQZ}mGZ8+^XiM|^!K@M`fCws@i3;&*7~_SB#KIpw6jDK-z zv&3y|Id|`KCkeyYCrxJP&o3oS;#pQWmnjrUnFw2(q)?<|WctO&ZeOd3FXievxTONa zV9-X*l@MPinG($MVTep+GWzAR*%-f{gD?~>O~{F9Bl-WG5LwLE_I_SN5s{|4J@aBHwngUd6(FA`q_O&qS@!Sz-U znA`B<^I)`o-V1QugYOZ;`1aQr#;K3v9ay+e8MlMWdM>!_7vS?f#`D3+z0i0KK7Smt z!lSp*G|<$exZMB#_wV}cl`GGfzu9;vTwX>^Y^=`%7G+;y zEd6&WV1Nro`Zr{Q2$(~$ybBj5CQitowehTRAq9?Alw>)0P%3O&KImHqc%?d3lxta~ zH4AQ98;hI|h3K-R3QYkL`usgSX?{>0Gl}g21kDfn#$2xNK@Q;X3VK*=7lIvfT6FfT zH&!h)LY+U1NQZEh<4>AfEkdxQABJ!2FjE#Grts)yw8lop1?fVGJb00(qGhx0AmR3b zVzWirj~VuZwjP7*QH4vZA#2aMqm<+J!G}W!<^(5vLdyF3)sejDtq!K3J>Tpg(hB}`5(WmfDp1$nhmx}cdK2Dk z*qp;Rr@roC_}qPudb?Yp4d~DL@V6P-D&Q16=?rcG&GiB>rQa}kbyN3MFhZSh?KPPxHjgeH=o#C?{hOUjv zw~ozjXX?tS?RK>P1Dfk)rIdcd)~So0wE1r}zo`F?EFH@Q%6n;U!deK;U6umU$sun& z4KAyct3a<`H#b?D+6o*=g`%3BT`epBvct`NO-~{Q10i;X9!m)|t{a2vzBZ8F%$V8K z8+W`Q!|w4EduBtDx_I;S=}fArE+LQi#o+ZgU~+SUMKn(N^x-ZsgWoe-Y4-0dJqr}F z*7wZQ$fJfwh1f*fO53mFv+gHwB|_Ff+2_E=R{Ks=)b2KByv?p+kv*-7b!*~VIN}k) zC0n4HJ5Tt3HHCb`QR1y?C0nV;y4FDRwVX=maRcfHZeK)7I&vdm|GDk6#>3(0 zQ52zmX zxkzPJ8og5Iq%m;;iVOAGHv4L5XADQHo(Z}SCBS_QZKQ-*{MZN~Q(c@B!y9D~hcLM( z1=n{;UekQlu`D;|62ca}EB;`*?mAD#xGcCg)1in69Ex9H>1T=FkKbDNMlZYL9}8|7Gt9USnO zXB+~<18aQ*@Kt14fQJ{U?J1IorEKpY0XpYAl%GmJ_me-t!blD0m<>RU(7ZFBd`7Ow5sGR(b|8MRk{^)6b3wbzNO zJfoyK9pK}E-HiN2@o$Ter!L1tZMJC5olxL-+C@3~@D+iDO4Sno*=4PjYdeU&=32X; z{>bTm(DQHS$S-w>LF4IXI6>_JJ{>KF`mTjcMc}vLYOH})!#@Om!=x?UMPS6KJG{`Ma(4UHem8gsf?((}qMr_^9uwY>f?w5Ovu+E0zK&}y z0N4afSK+Sf>07>|X$$@1g&yV{QUve?@reQqpVnN^Yw+}q3mKi95+yXK`39!WalcT@ zHo=6?OXJSecO=c^d>uL_3f-*cZc-IE$QQFj}N2a&q8L^@&3}+ zT31@A>k+<&zIU{P&!g?l=R(iYfVPi2S%;o`3f?MO|NgdH0bYe+g$L%4f>81d{G@V! z@EvqI-G)p8YY3t~Wuk5*{43YX*>Vx71q503^6cyHD#kR$_U3!$N&}VRaKN?m&%GL8 z#`CZBDk(x$gPyOiWgFRVTl}tD1ND8#{E~f*t8muv+b7lsjh$mGa9ZyaH#`$$LXmC! zb46Vfxw5^N4WmZ!U+6r@X~Iu8eRCUPPHI=Q)5j<9kqZJ6EzN=Fk8gs@L`eAo>)DR) zI&u07kNczYKVzhVVT;p*SBf-Q-g$&Phg3~SSUw~?7NiEz4nlq35XTR{DxNUHH5|L= zy2AJdu*B@~fZJ*7 z_dFZ4;@#8XyNzl=XS3Y*+K<=ez=nO|xnpV#hkmR`ZWlCd|2kc=L810Ye+1&zC>0Pz zWmQmr9pCB=WX~M3hbU4hcITKR*)tq6IVW!;-MBD*j{S&Tt$EA!I7ccXrt2t`Eq2(@ z#KO&Efg1#N!|tG}Y5CBf!hd$<&4ymX+`W8kZ3v&q^@#(Rzkd?=u6rU+j{9E|0VrvN-1q$4P{`Ku@&6ht%bZjqE5pAoTUIuTU|@>aU+R#mvI{>3*F@8Q#E!h!YiX5adeO3IW^Vg>`)3k+h69 zif^3bjaS|&Kjv>Wtd=BTT(^HtSSaW{?1fLl*?(wXID?_T}rRKfHB~)#MX`OpmO` zfdq$$axjqPal#+SN-%Madgv9 zKaxnhevnX%h)S*XZb!>a?fByY5;`#accZ3tQBs<*!u$h4m4AW`i z=N0$m@qHh_wOy1WE1~2bGj0y;IN&xr(Y%G2 znmjtxbV2HfbZMyL>*rwzx@*b!I(#_LuY@dcy#a0{bH%M@Cwv_I72=- z`*!Vq6so9}Yvp|Xe$<;dW7qb7m_UuvBi7En_;0?-Q@=W>mR>kOBKcjX=KiSuyeeZME;$EI@!lf+ zyYLFTwpKbQ-*Bq0xVKTrK$lR%-12s@=DcEY{*`Xq`pa(n$`eh!&%aU|>-;O-N#phT zS4wy;QuV}@m9;D9w_&wsANu)@Vi~BpO1Y80J@S2#Xe1MPC}Kxm8+lXY$0JWf{#E36 zBA|G#{-+-xU3+=+8#qAAKtN$I<@~{V&n4N1qqFFBXqo zioH5ki@hQC}#?A7P~F}{P=gpUmiajzYxC~x8n!# z$KwAq{{Hx{#(zEjvG^aw|9$+K_+Q2Ud;C;lIq~Mi+Y>*Z_?L+fCq9<=KNG*7cslWg z#BIsDllLX#$(7`lBCf|^JU-DOypGbZx`RU}pOa8aysnp9-Kb(rDmQ&|a z*HZ_nA5T4-x?|?CnV*??bNacn_sqU!?wxbLG525R&dqPkKQaH2`Ev_TFMMg?zNPfi zQ%j#;GVV2uw*j9IB5%d)zdxFbJ{EgvJRg4-M)uOgVdD1^G=j6qk0t*yd3Wl*)Y;S@ zr=B-+edd{&Z_NC?^xscEk^X4<3+b<}O{GboRf@E}z*s^PkS7=2qsOocq1G z$L8NX|HQ&a7VcY2FYYY<%Hpprethxo7QcHbzjUzlzNLS;^y^C>U;4u(3;>&L1Al%f zQjI(r`GM%&@gImk8UIB5iNr?}x#T}erc$e^yJvo2CN;Y{`}FLWXCFWFOK0+Pjk#Z# zd&&IPe14&US$%oo+~Vfq(~DnTd}8Ut)TU`5yEST(L>iIDus-+2lbF@l<4+_%%-VY@ z^|{n9&wOY4eER*E!Aoa%XWu{jmD%%We*DZ=&iwtk!?}Mo_uTn==07(7%>1+SuU>d; z;qisr7q=Go7GJz{ZfR@jBTFdNVpP!n^CIUX?*hi$6MZ&%XYA?NS7T4dzZ!ok`E>Fi z^;oJ57=0xDhv_e-zh(9IqA9dhtkic@0@+bY;5+%Y~sv&fF;`tk1m{B+FW{i>C4nN zCQ5x1EboiXM4yO$D4LJeW6#E(7k@jT`i=O#iF)FXfh|9nTuxq1KAOB8__&>_rrwiL)!F;bJbGqpZV&7DKjyaP_vU|L{`2z( z3vXTcHw!vi!zi<^lLCZ3yoN%Hl{_a+}i%U_;( zP5L*}-$)aFJTm*svmcrL*z6z8uAh1Q%m>aqXYQxxerxWp=3YAgJ@XIFKZ0HG(fKzn z{3%xRhZb)vRuJE zb90+>_su8gA7A{X#k-g8TY3^Z$#^enNMMXlL_Q4oq!=bVH-1n2)A8pfUXu6~v~y4L z&y)Go%~XEo=FGD*&r9diH`9;JzI*nuGw(X{?3q(@ublgxxi4dse?9k&xxWGCy=4B! z=HEa6)coh>7Z&V=Cl@}s@P&oHTzKW;`xbwB@q>%MwfObL*Dn3olJP;*Vn)9&io85> zDe`^Mc=Y$8e-b^#u<)C)yW$)1zl}dH@xsK*5)UPw1TK6b@mGmN@{7r@B>!{rzbB&% z8-6SGhpCs%6lWSU|7hlIGasM1FP%cmY z3+rs}{M&^;TlkU1_bmPb*7eE7zg*l}+FN?V(mR&^l*aQJ)TG0oAC4p=bCC;?M;Yte52b_>9Po3Wpa{aoxTvDx@CX!mCPC*uUYe~LZ*)#N)<&!oPQ z`qLR&!MWLsfX&t!=gj{NJl>sq{oGsU{@vU|^RJnI)51F!erNGBi~n`W_%lu?jL1KT zo{7EzC&_1!>|rhTVC+X@KLd!Q;!ngs96w0B6;Rnses}7JQVme?Z>F4?(oB8kP1rkg z>5J))r~fE@diG%Ujk9l^{ps0H%~GGcbCtR3{BO)ZyKv{?Id`z|mW5{* z?pQond<$q#V(D3G4;2H5=eZ;DWb_l!r(&OrJs$t1cs|icJexR$RoqMwC4KD7yBS5J zRr$yB#^N1|cP_qQ@vg=1T)b!Tdlq-79UQ!;@#poCEu5*3$A2MyUm~5jd*%mcp25z& zx%Bwb2XG$UMn{tIL(%udUdph=ipIuD zex2)FCs{veSXn8BRVzanR;`3ESqa0+WJo5-Fquq-VQLtLsnMh|iArHo5fIWarD;k*m}!}*bCvw$iZpbl4;f_{2y!+FyCwH=(| z{J>r_TAtQvw)hquIR`n+<^4`fa9D6@a25T`a7VhgyBpn|?y0gz^rx~4=?=ZaDdB0n zg*x$jirVov+(eHUr{pM4D5nKh1x$-df6Ihoo1EwlE3(oqem&#N_RisonwsEcj%hgL0> zdaD2{oyK|Cpf`ZqzS2(!+{;|rNrxS2Ja4QsYK-@dj%GV6j*hX2@04PzwrQu?^LS@I z=Q3xG^S09`m=qik^armDZet2eb!WQ!+@YaSq0ymBKpp}IoEx6P^ZF>fDclnF)wJ{R zr{8>u%ALwG5oITAp zoRK^QgSWH=TsYqC9ZCs>=$`j76Jxy|-pSsD-t*pD-WYykQP|he&R4+inyg%}bX8AK z&sN82Ibhx*tyw$6Z~8<2bHE(E^%Om%kI|d;5rK@rxWLrFLxJ^yra+mo(fHaJEwRQP z(`QY!W?0u*H-m}B**W$sr_d3VB4+aXP*Z46$mgYdk9bdd&v@&+AF0Ig;j6;)>7ng> zWqgKC$^>N!RWeg~rmb_wsgu-a)tA&)IVtV6$S1o`+o6r|PXuvaO>d3X6ZAp)5b*UD z{TqFc{;RG976eKH&je}$Ba8=)6(FJ)jXmJY6fdZa#e0;@b1K0DZpumGw%K z@)UjUfVxrZ>sS1v^(#(a#*m8^6C??wFppMq z7C2#}wVeu@Dw)83CyMjDGdSJ7&Ry$n;G3JF>E3K_ZKS^pX)ov3ETvFcKp$UgZ=j!C z$ot#@hnUU(`4y*N9RKHf`OJEIDcejex_X@nO>v+NB$gHO0Gx?A0c zLaRe(c^7y$c}92`{opI{*|OS8{CSV^sIpnPmtNp#=g_4L(91{uul)DHEw}J~9}f&M zGK|HBVU9H4Ge0*U2IVzb4}q8`!bz5bVW#nZQrsc*?ZvK6S2&BlP#B74iYsuqd0ujO zP}mQ)%jcOKV5YS9mGJA2RgR}7s^HHXltv|%`JS&9F`rwQqha3R7-~e|VFmnmhrh2r zP*>;{GdUUC^-+O~0x5=NTx-lSZVr{^jY_~P(S)tAaoTo?2AxTI6gcy?3*8{)p5!+zDbRGp%-ZPy22AQ@gwK5m@fF;Qhf5C4247PZxFS_s(j2;+q9- zC~7Y`$qFjrd*vbZ8TDLH$b;H&u;MoVPyVO%SM*=>=L2tn`DYnxBM*_(<9Gzd3#?Y&vpT4?Kx$ga-upwO;v66 zT6LrPjrs?eV1_o6(^sgy$?qNS&!sQC>d(?I*RRp%>6Mam3=TN-x~9N35PxeR6+T)* zx2QrP7-wE;PBX7FS2M#CtihIJy=C>XPqLGlO_$q(&(|>1NAoGmoi)zu&M$NWD|mkJ zqTosxN;|ic8}Ft|F4X{MO9r3a%mlqJ^seOk>E0z?xt9>`8$J~jIYVj<`$WA|wD(o> zord$=UZGk(fH#X;xkp__FS4{VwGmpTwwV5X0QQpL&qf7!9Ub6fe-}Y zra%ER_MO1~K#Y-Oq%dVWn6YLL^JKJ%1!jr)oVm{IV2y>vWWy_-l1hkTTlOvXCw8;F z9i8G-M=-Bv_-31&tsvaMTH_Q>>^!eu9<`Q$I`HcCY`KkGh*@anu0#DVjrZ7XEg#T}~zPEnnb9A!@ z+W~t7EH&4@0o=BfI^S=1a?XI|oacfhpHbl;KJ0(UrRyk1#E5nov zmAUYxJLxbh;Y$P5bLoCps|%?8O7#QYUNQ^;964PIQ5Jv$+VwTw~Unb!NTUU^b#mG{fpUNPM}MX}iEH z5i@pcdtW;LtX79>W6{}ufXxmEXA4wwkA1Rp7V|HYZZV1HIMdk%ukIb}PrrCBxPvD) zJLFK8v%K5AZK7xHYcH{XN9At5fkBtL0?qCOe~LeqKBd#cZ}*?5pNWz(jXu?)M;pBi z$GF${(Aa9UGf#rSKW)8g{b+giwf0^17u3|xb|18zaZZ-A%vsT0Y_$-iy@u0yjNTI_w*chZE|44u1kMbcBQ>X&>3{D8 zb_Y7494f}y#=~H(4Ms1zPLcTn@4A6^EV|Fx)&_qo|GN#rr=_@%frE^nJ`iA2v@wJ zkK8_?lR~MX9N4iyXD&~4hu2Dpo3)fDmwMoYCA1j z>#Ci~*_f)mrS+tv)%l}gf7cr?7(W?)I`L)ZMC(p#qw}@1Huzq!FTCVZ@X_h~w&Kv6 zvhttvs=UtOTf+B?@0`&=o&gixai%g_ovOxZN!oc*NjMj-daM5&{VKf(^uK^P8eoJm}#8eab^yuHQjD;^1-na+^bRXesTZfu0g|W4V~s) ziV~9)zBT-S`0Tln-)AVj)PYR01nMb{&d^DJQh!xXKs`(iC;>fS1>C?8bnlUYbl5~D zYSE;?^uX*ub>Lp(Br{+ZTE*6Idn_}`aE3Xx=*=U8e@JH7GbFUj(20{#N5AGu!&sy2~EqyzjK5SEK~P5^-G=d@NWW{FE99yYt-- z-MG-DoT{HfVee)X?4IFkI7Lgu_bKWiWM=e(0EoJg8XU&Yu0@p%aFREI)o1#r+cQDa z)9K?K=|cNK6-(V6p`SxLy#4UjNZpA4e-^#;!NAL?`UeBY8MZOOm;*a`+4#(8MR)w! zy3n~qa*$(#z2J#wqrqLlPwU_c2Di{1ADR@(3T0EHGeWtcJoM@%p-}kp@Jc%Rmk|xO zqJzZ#9hBb6K*f|5I!>F$=WNt&38t7BJH+=~ z>93@NN9%obS3e7d`D%31_4+3LklulJcLwilL11a%6%f`}f$z|m1{kI>$(U*s8h0DZ z;RC-IiRLNP%z0)O({io3+1yT#?_%|`hFcfX0d7MZewH2(ZTGQV`z#pZ)%JW&=|*(G z_H=`RPJkyl+bIQ?)S(A%<;J5z&&az2XZdxF~!M0Jik)?MH};8weDp+1PoI6QP= zXkO?xy7IH38W3%7`r}Y*X(`e#e|OuC&^P?8h#x1@IiPhv#^72zI?}q zG7M}IkT7v!=(~+u5po3ZqypzP%j4!4NUS3)ATE|H+t#-XRh;> zGb^a#WpoVn54qs_>qB!x&xhU#?F;?F)9B>|VBQyaw|R@aH@we0U$_%pqCX5{65Ql#O*x1kaVq-RFm;+bPrXfjL0zZ*0E1IB zlP5A3th$)q@|N~4jP5x95dR2z)ih>oseiqHzyEjCz<@qhpMh?%PJcs>q6=gPiUYNQ zuyLtzv+=00mhUD!plGX)Rck$BpXB5@7X?=b*Snv%iF~8!U{J#w?nU#tcSz+jO7=-Z z9jOlVkM;}p)nI%N=;^p69d3UzfsUr9#e9o-!m$rq+4tTr;+Hi8TUCfUK$ zTA(k{$I%fD^hfa<#7|yNcbE=}T_4&MIutVLE}g=u;mhFFQL+aPrw3l83|B8?E)CZ% z)aId1{Nhj4mj<2;)CYQ-r<&>JQqJGIR&Uh)b0jx;Hz>5Bb3#i|WIh8w3<+NlzB0TJ zeej|1D=1Mvgnwc7Mfpx8yn~uI0 z0@kkSr>i)}8Ic+i>i2$7)lx9B#Di4Ma3T*;CzI6w6i+f=0#P9$Epuua@aq^$GJm%Gb)TK11=SqM2a#*Wp}es^_WKaYkQL>-ZeKm{nm;Xr?xsH?xwC z^_|wAvzg_;jOq0jc>NpHz5ef6jm%qKZ(JL&TRb^V0;In+MiosMY&G8G5gxUlR- zikrW;FiXg3tpf_MIdHdzS8WNB9N&|61m3eYhdqNFJeC%-f`|I+_8-M%A8 zt3_7>qXSo=A}$KtLjq(!$&V+{^*=INV5149ZC=1Px!9UzJxKTHXlJ2woaUU3yKs+F z;T)jGhT(wC4mRRWKH^rOB(CF|3_v%_#9yung_!$0ykEQ%(P$!l>P#gA@8c&oK@u2Q6XdY$E)p+}NN9Ffk5WFOK zZ}15c6|V)abboU%3{3-l+#7l#^g`&h(C1QHiIN^e2Xw@~N-BE8aOE1jvd7WA-cah5 zFO?n2v0#g{)QdnsGvq0Lj#K=V`YZe@78QB`e(EgkHf zN`>De&-Lf%bGw-@(fYdJRiXFs)DMI_bdk~EyeO%m4ncdi)eYKDC}=&Yn==9za`x|+ zr+*VjtEX|QaRynN1;+ixlg6{gCeB4qdfgdPd%B-)_pG^z9^R9D%o%tcYf*UGJH26< z2ApDhFdo(HMR&8C07u6i2H!>bCh^btFz=N(IQvnP$7$KxQoM_`{&q0gWZa6iWPYZ> z27V+>k;%lr9+Vu1J3oysQfaTVZ*rHpuhJ)PK?D67-u1miK)!uZk{{1j-dA#P64QAu zUr=Xaa&8KI2|_=_Y3NUa$i&B;WL#$4jyn7R6SNwp{2*yG#rTZDavD-E-tq zP9zzV8n(mN!W-!6Ssi5^u0&7z!SB<1quDO89^an6 zj&}`7l>5=Wp2J7{gnHcT{R(gQ<#v>s`rG)oJLnL?MrW138l+sp^RHXDwd^b6^FkK=2;#*`mm`_XvM zvuE>M-(tpZLE#+^hP=$V8eR8SXKwJ_phN0wrn|zebw6;QrH&hUdVhqv@Et~a=X)37 zYL|GAlJ==<`}7H*oN)LY=6xXvhGp>jCYb$qXdE4V#k{5%vWw&3UO&JmT7rbiog}9L%;7%=Q7kz@9*?(Hrc10eI#Pw9Q>4fkxngE;L`qY5R!` z;a;nnWM3mXS+l*>Zm|#9am>gBC(%iAlAWPwo!PimGn`!5*dpg!`a*k`4^tGWnfZ!H zYP_wU#^+J|HGZ8Md`CY%aCYE9bPgN6;WW^BZ;AN z$j|Mkq|n30E4SijzDW}Be%|&cxQ;zx)fbR}xu1;bC;rd;J@w=C3-k&4{rZD?H*1Q_ zP@GPFBFP;EWB$zT0V12{<-qAXN^aYmpIfK=s`z2%>G0*N{B!)zf&7y|Q)h$wZ`1Dr z4euo5XQS_q3rqTxwA8!LP;63BHtNZ8brmZ7XX;9x()XOpKeVo>@S^*j4%3~6nzWP5smtWc z2YaU2I{CgP>Tx)p$^Fjn&J|KQ*+~By>W)Dt{?L7hv-2|SU>k{3UtF}D?VFUPvJxH6 z#ku~6{d@dF!HBouQ&(_~6RbSz9rVCl&TOO1CR97?VI?0qP0k)C8oqZKY;Q$y5B`@+ zV)Fyn#ben|2F3NxK}pJy-pWG!>}Ib$_&fxMkCvX%O0>t1mGO}=8GY-a1BgriT)7Y?Mw8dU-Z)h6L=E0qRM_}{y>WG z5ZTQb>liEEI^IemS$U3?VU2^==7Iv}S@Yo>g)rPAw9*o5kJV}&wEh4YcD1{suAPX} zmtv=qiqY}KT;AV$zViS_r?!jXVXaO&ep4A)g7RQRuo6|OIw-j3M%dW6&{Vor&^ysR2?ca)8=2N0&Z;b*sPNq7)QU;@pO;)$41K}GtXt#jOs$qDlhIKUPd-a1PND2dg1V$NG8AZlp#xtDvPUt@On;pp|UW%i4xpfPA`3~zB zOJ#Ok1lGMyvgN+c>F5?2B#_tR>U9sEAUXP&phzOObGu5vY$)jKCHDtV=<%T>YI7+V zVq0hzeK!tnwEz_V5$x($^sgS_pP8@G61NEMT&BDSXY2zvPQ@L{K(~5Bsuj(oUyo;M z4C8!k=Pmt?O3@>b80Z@q5D3%hOe18>Hg3eD9BTHqHdq(gH}PCwChz$Lsn=<42A^RS zne1Jm&A4#~QRn;8=f}x>lrJM%YJZ)SL>QflYP(;#PAydL((Z%5y#zk^5lv{GznZEk z(>LSrmjn**>?Sj>9x#qGb!q`Uhc|r|HT^p(<5(upN6wFEfraq1<-sR;ukXpsVjlB! zGIR3=@?y7pcY61D4|7Oq@JvJ?mG zvz&XK?aoP39lj%Y7f*aYxFQwB?U;}gI+Gf{9KTv*1`dX@1kRZsE%DvmoWT+@U8X(^ zw)Hl@YZRz>J!o(!zp0K~&UY9ofMJ zH-9sdLn(^(t>V}8P!g0^m3?@qikc1+oT>KIQo(x61+w>v{=R;{KkUB{hBX;ygI%axjpw^Wc}z)x zt=6&mFi9#zD@m-bMl!MI?tTgDXV&HLcLZ-|!IT4b|&Y5hs*yy0)~ ziyTN-<4P*%X&98?GB1SQLOX5CE^m#N-p*HW;KAy(Xk+7~%KNDPQDAFeE$PV*&92s& zWTXe+LKtNEpLc6;eV=FM{4RdyzG#^tG{_lGW5?tsSVt57(;2F+j>kE_M}3-Il0#}Q z_{T(C^ZT??7;6ex^cNWa1ie~61~)7nm;AcGLiR7l8BfE@))~ig#&XP8tT&}fcou$0 zK9#u`C+j48OK+vWGK>9> zjo|wZY7UI-2lWTdhaz-?e;yO)Io$;Z*9FpX7rr;Tk#qRa>d&{HV=pH$x_am7EkJZSm6QGD_m=(#RN5A5 zq?dY*^f@ZYTh>x{^<<_S)h4xB9Z%L*;DH!C$l;{NpDDM13Sh zexB$|9Rt^ZHjg(lja*}|G1lB-CNM)PtgdLipL2SzLvtJy6jkfOF}Asbcw!3Lq7HlU z!WppxjfCJ#Ci4eC_e zpt82njiPa}uXPHXzCks3W^jjl&^-Yk=Je2cbfH4}P+e$CsGXOL3v(t|;xke}zHm4E z`o7`Q@B;jNmQZ*EXfT&7`)wf22g8ptbDs)7!zM}-3~NvLC-J?9bdq`dc1mY*8$C&? zoT!{i{beW@D^sb#IqXo}hAMg&%1)zFtDP(L&d>cD!F3&tF2=D&UzC7bjg{0|g;9sL zdA2#*e4egSjK}bl-NEVR^hEU=4c?!NuCyM+9~(R|cpAKRI{P?zk_VDEfwd%v&y&$f ztSI=Zc#rSP-KjlP*M9XsYBv7E$MgUlths_d5U)=p;jo%&dkby36H4725~NR{bbS!` zmXuEqeCUbBO~yUOLq?fQKzMjKx%g<8p(+n2e^H2XR|Iw`ajH-x-*-Mik^GiZ@e}9b zcc&94bY!q6sI8EliYa91CVOJPB{w{iC%QDeJiJAG-mFf(G`x%+sO9;}^D?8^kB9TYHHznEziYZQQ8s$}-}6Jr|+ z@m{#&L2~__NVf>z^LsL;(N+)ZBx@i!p`q4!)AV(epLGa7 zAf9|@D&0Jdvo{H6G!LI*F$wp_NbkKsa`0{Y1Ng&M`&;`*+|nrCRBy*6Rg^~BH~mO1 zM({N&=yF!mcpmBOa{MD7PsVN@r=b9Zu$b+!VmL$TKd@j;uohjkJ}A7hHK8t?l9}O$ z!#|2=nA=HWCWTzd6va`WQg`8p4+7J7qND7FW1m9yZbBEIvfBt}NN zH@f$rc$d4+<2`R9r+x=M>qc1oAi>t*#+}4Uh_6_wA!+k8Nxsh7v6`lxOP+5Q4rv)l zn|HMHWwu5*?Gr#)cgi&1P$uOWxLkLUfk;7bOeZFSA}I z&G!}a;SXy9I(;q)gd5NaAAn;w*~8%LbJ+K|8{SmnJnX#TG=ukdgO^kBv&M3Ymj^E- zF_MK!dj(m7J4h5gi&E-%7sGGn;tEK#CzJJ2Kmloa3U`T_swC0ZJl;1S@MZ!F}ePCI8R$q zzjyiD>z(vi7`ewjTby)Wu0jW0kBdHr)ZO8 z#9ZeVHkZB$4hDCgEx*~KP&(ga8Q)~57l($>N#=KAaEo41Ts~h)Oz-jDHIA$2;KEwLlUP~Hb3%Svq>^}71WKCehKzOnDx}EU}UI|_4 z6|>FuDV|3nIl@R!?4UftUV*61Jg`8q`XJhGwR|R*Yb#J`zo9zg{HOCoGMUBAewQ3t zAzc0mHX2TqxGzYLS%U*r4Fdbpn2j^@9<_SVOkk5Q1&3)myF&L!W;X!uGJ|(}QSf?Z z^lKzrQsC)J;X*SwQ)@y!yc4`M=FZL1!`O>@w~YBxhBw1@W{u})TazkY_Ug1P^ryI$KH0c}3VEu=kz!y(!Y3~_xK8obf?|Jd4Z1pDh|_Dyg|{$r=l<9 zkxyRgM$mhTH^|fY^rNZI1K!DUVvb>NU@?9DjqrO^ko$Jpr@h?j69 z8MkGmrLQ&`(e}WwWb>8aXcE_(NnHO%y5QMLgPNHd z`#SmR`SCvZnM2PTt6U1+%axlx(?JHs@U&{wxb51v+6{2tCFl;XQr*A8dtcz`Y-T5+ zSK#D8f6`7{s ze6xSDejTUe1U#zgfje*(4g_xFlx{M9Wz)0|UhqWr{7TFf^s4vJyE1 zpg_mi$J!^8gsEjCrUSa-EL^Kk=&TdSZf*!B;&|UfC+&kt z@OAimp=%|@_*%gaKJZr$*nyK8X{X%^RFSzs)0UI?q3!cUj0r zZ;`*4saT39T!shrw*Lf>jYFbvt=@+^bpjU$t^}Xmfja&%Z2arM4`@=I$;b9J29aDF zW{fs2HZCU#a0|0aY}h=FQ~4@A`$JrZZN_&Xp+oTLW2lTshA1N6qvn0dPdV^(71UPyO%eLBkQB_Ql-nBEsKU5nJg zD794f$ZcA0{TY2e`}?1>8}Sg0~nTI`zSw4y#*p8&|hgs*N;1#o1za7o4-%$$P z5Rl(UJ00|w$*G@&>XpsOKfy6M?O(9Z6%$P6y!3Yaqtd9i^IrM24I+P%z`arzYWl_q_M-WA14Bk6Fv%*-0xDXYnVF7o@q|{(G ztk1P4fIK&%?0jb*>l_cOxs$!WN>Tv7qqitQ8(eua6L2Z`u&>Ohk6;#l=l)LitUEg; z3q#9DQ@)Sx(uv&JDW2+u(Gn+;>RsoRg|~^bnh_)C@jZ$~R%IAF^#`%pa~9x>%-To+3f}X=rz77Sb z9<2H_li~C5Z|GVv60gLw13DIO@Ft~#9JHc(ytypq+d?)U%fO7U!$SA7!@L8%?_|)( zyQndpd14M3{;_l*-+bFF^c%fMWZa7nygO51Vh`3QuLn`61qIJ-P8H z`xMl%2j#YUJz1Zhaf#!c6P?Ytf~Vn+-$+*Ras2Tw$bLjIpYzy>Ye3E3!{_(q#z>4k zNx4t?NqtZ21Wp`42Ky`Yp{D~s1w43?NE&{}+v~@MOC5XueXNslC4VP>V%Wp&iT16a z!CH_@t@9tJ1J%=?9dwbBk47nts8eJDg+Ght)?w!8>^Gb)fTX{R(zJR^wr{k`fLvT~0xjyNIbB&DnVf#c`v# zm#ob}vlrj;G|O+I7m=sX~ zI}iWSg7BJ8@$3V5B8BBml z2jFIcNk0zvG2AueO%$}sDdz4(kbQSEQgHwRWw4;PyXPh+FiO!b{?d-RjZc$ClDWu!xQkPawFj}Q5| z>)}dN?aRa-X+w;$S4_Wc(yHBw%v>)%b zCw}KdHgg`L&pl1<_*;qdhtqGTpm@Cy{3SSquA4_bXra5-eU}`@esr&s(YYp|Ge3g@ z7e{6-joC5*k70@2e?F62J&GkRjr6*k@j1t%?H8#P?021_i4F8iw1w>1K1Nj?%j_21 z=tX&B%pd*`s`z!BsGWMH(S==#U1k(JN2#a?^O+udtS3(>;zmDHggv zp{FBgU?0V^>r*EJl#GXrMiLX1c@i+tI|CGKaFq9iWR&d)$dsAb+Uv1t(?R{a5 zVh^i`O~*-0h39c+_qrZiSGO=1UJbR#XEU7%TZQiSo_IcSon?QHQo5i>k4LM#L+Qe9 z{0utICKTyYP|q*Zu7Nji*WSg!eH~2w9?#`GBMXN$pH%yNdU7GTmLj8={kKw-<}xx2 z=b&nBFi+u`DQuZXb63utaO7dm`OakLWfY{I!Q;_%Uu4_rx8ON$kz0wT-N>$6GrDT4 z)HN+q$3w_ZjVF7*GW25T0XCPbyw}CtP3kN&h&L#AD>_={)#_~ZA?^=)fhqDHs_!24 zAPPix?L_S~(wAq#gC~$dxt6W!h4Ln$XSA1@&L6Phv=!FyBZ-|Te>bu*C!-ImWSahO zaN8>OU@DlIRk%YnWaH}m^=RIW{w8MrR(}h(2<)boTA5~tNMuCmF|h2IKx`n6v|IxI zRT8R<*vE(gPhCfL@prd=Nd(d^A!y`-^d+0)5#jOV{V?UPsW2>NZ%0~sY|&nNBAm@^y~pt z?s}M)Q0Ug8xf`Tcm&)CwWctlpDF2%Dgimk>vBTa2W;Iy6|BTME9}fo)?pH>EAYLJR zxryEUUhu)Q$c>LfT_~pewZq{$jrlkWZod)?)+5l5`<5PMOZWhZS`Gf!6?eD4H3;wX zN~T6vTR1v%NX$%e%IHLA;LSD!C(xbe;MZ+H&A1!|{DE*q+;$>7XyG<3 zf+dLVcNwY4GQWr4dNcbP-@(qiqP~qnQP@R#x4-Go4QE-mb2CzqoY`gIg)i+s%m?8d zR`Fy{Lh1URK6e4TF|$#ymvef*VMpd*sIzxEy9AebSILC#D!j1nAm|bFyN|`R%Iz$* z^eEU@7>0C-vRK{-GLHSb#rS`vr1w52W7vVa1#Tdj@d;0<6B}lUbi90hW#Fa26;`y< z%h^np?m+NZQbHl_wz`rX$Op+aZg98I^}B{dbxtHPypU}4CwTNeaOOaI@Y(Dm&BWb& zjE#@?nPUgpeeBN!8;v(|BYO@{gj-a~gBEoMQ}KJrJlB%|{lJ)S z-eGPtyRlikguR(sYZG~$uBbnQ;L=O%dOMqmDE7({>EBEv$4>DS^rEz>{Nufy|w1l99nd1p~P=z4*%1f}L@VRS9+IwBt@t*}kc^l;04A=OEZJJc{lm#eh;--O}hkd4+ z&eGTEL0)$xn#rrmE2sqT`M>vfCPAm+fLz87;$0{P8^G-Q$4gu$$oJT^7*IDjI z=}LhTr54bu@<<@$@e_4YZw2JjiVYoPnMYsIIR(jVzUWLF9VDcqYU?CXb&!9n_`3K-WS4U<%Ljbck@UJoEgKAy%uYozS-NC?c03AC3;UO~RvI;&On$JI zpWhNJa9i9=@Px8c1kL%*gp=YEiX{cm3GfSVr6W#)T=t0LUVOnfN>5Af>MT=V& zYx(rWY>yQLS^^idd3d>T4La1#=ul4?Z?L)jGrzaLIf(aXquvigcNtA?`(lz4mz&qf zJJ4>Sr`|={=t1*Q+~ucnKVLFmmFQUP_HU-&eq-(-8+p+DgZJGP2T!pUkoxQ@Hv7RI z2c3c3Hno70zlx_-i`Kr2-GN@@S^K#QH$>f_yqH8e<=Huw2HQe&CSNVmi>Bh`UVv|>c|DVNX?FH(^D44k* zyqkId;#P;{pxz4gX1#zDRYf=0O{&4qjgGhB_nl9Qei8cMDz*puav#M*xL6xe^g5Dh zI|Uc?b-3ytl#;G?9C@{=FurBnOtXew+-#>h9(*;xjTE=QPIl4bI)g|Ia(U_aZ_C_g z+*ja#+gza)FF*s@NQdjj&gaQ^1%@|*EX2t07@o)*cvqjnjt+rayU2NOfJ!o$_Bmu< z@^CyJr@vL82X7+Hxfjhgh7@1CdOY7-+{d9Y}o z$hSX&ezcmW@Hu^Fpmq^z?K-^Bf&Po(U%UMM^mEwmf0uomp=3Q)FzbE|3`E;lgkt(5 z>dhDuRcmk{dw@W%gPVSAwP!#6HlD+Fypf@#KVC%t>x1T2K)r3JgJ`(i<>;C^tm;1M zC)T@+?T&TSLqGDvi$P(&Azftu9z+u0W2J{WhIduVryIddrmI0;UEmmtxOJlksiS2e z;(nw(A4TIG$Z2}gcoz@b#Cv>zlhB2Unn$Xw59;I?p2w>{S<3RP5p@fq>*^O6?NlHZ#&t^e%wYeg;e1RvQyhd zf63?~b(i*};D&NX=xR3X+pGQ9Gn}H{ic0<_yAADe=7-YJ7m>34Q9GU_?4xK41Gzil zM{eJFGtiHO^J?RBqlf8m+r&DmY#Jm_Jxe=f<2cX`#vO z_t1^u)#1J3JF}#;GaNI69 zDz~#?_#=7vv4Q)z(YK3nKIryiDzvY8I=ho$l;dmJCwdcnyW318-@6vw>vG$5u6J(4 zZ_a?@e#q^y6Y-hfB-8Rf89DK^-uFIdSN8;Rxg?jsmtADOyBDtHi@5M(aRQo|o#GRX z<_?cv{g?6?`mq^T&a*iKY+uT6+)mbdF0(lbwZE8tt8p&gMIXG5+l0How~E1$$8yWe zV$N?5_7(2IiR*#yc8^d1&;h$h{r-663eM{3q#OpqZMt!v$(Mc&9akaAsgN*I%qiwF zaMK8sg=Fs1TxItG6J6){L36L5;`OJOjpMnm!#%i&Zn2IXqinRVfj} zDfjDa#DQ#&uORQs-3n&mF~pL98ObcDRlfrVy`znQar?=dlv0o3z|GX-Io=2&{-0-k`FHP^GRvxzNhjjEgHU(LIAxEtyTeGN*_%)l~w(T^nKE(497$efvr z5C5?liwiY}1j*M--rlGkSAbcY$;n-eCiWR=uT!~2^Ff@QZ%F%J0q3qo!Rttun!~L# zpR*4kQsHIZ$J{FaGK8pKaE(WpR2x5Ph%*>mn%1lkilM#9H`&P7`|FgdtaWt#ZBLBSmXe+pQCzY@dN80+lT)XZ(U zi8u?%XB$T8D1v$l1}2*D2h3$tIq`=)@u!*d7&KUQ%mzfc7@!n zSj1WSt8Si&7O5D$)7`p&x|r-c&~BtZkmx~p)!tgkpG)&T!nVg$bE@NKjT_F;(8`P zqkM8vPK*96Uz!`n`WpJM}PyNTS%A^4`wj+PtFeXE5HR+BzMG*IzPf7>ZM!0jr&Kvdx0wAsir{^wme zS=^nG6POXmg%{>?OZI&DP$9{ZBJf8^pcE8P#x1Mmfr>z7po*RLnty#yyGiu7qC+0y zhJ`4yJF!Nb5sxmK$enA+Xocb}>f|zAV~CMvj3lj+L3VFEd!#~nkG$RSZFlcBvmMmJ z9gQiZ`4o7N1yUaZ*BuF7&tRU6cPDXcvTsONnV-mO%RdyrA{OI_6oaBm;SyzR36+DS zE7>Eg{!1=+D^qqS_n{vkQSH;B;PCtEPpH-MC z?^yb4-86AO*I&=cA@(|=*g1;DYdYMQ{@IhtNUxTYu&%`Et|rC$*VFZnzLe2bT>I?X z8Qgv3{&Sxj#imFsK6E@cKqPY0V=~)z|E+Se$%M^7cg(|6numT;K+bnDu1zt2fuNL> zM;W)Pmb(>hrCa4zlhdzt>p(Dv9i6Rc4v}-*%w4W6sOYNI@8AVR}5N=@@3BS&0JNH@KzLC>*`t!*~&JP!~b%A0w`AW%Jm7%$ogX1ef zkk$Wk140Wr+ip@2t*~-tXI594N%kpG+{PEH#KF!J*au4DzLgYq~$-*mhSw&)~;RlD$?+4(qXol|3g0Mzn{;Vqq6(T zk@J^Jayg&7kLUCE5DLkW6oD^Gz!=Nn_N(l2y8^ALiahh5nrklEhkU$*`P_U_=q)Ds zTg+~3DVj$aDX?;KV3qi8)#%x^~%TlU$bJ8Y`#s#!EsiK5LFl z$tF1&WB>J*wvnW&a#7m~WRj{FlvxelXlT1-v4uTCah?w&3>`!yvQxS6`h2ix0j`rc z&5?850HzRVA{I?1-cDqDP^6N^%REvJ?^L|aQr=)KdU*pr;Q=;=BeOJ--NH1o=;Kju zv(Rtn%d~zOcj{E2;Wm*%-tFu|3q6ElnaoXWmQ1OSL`%yJ=8;G*mfMI`?5ovsSBmHr zqD#cm8N@V+%#rbQ0CDysr@oAnF3z|(-8*q@W90r*GOlepx7udO+tPB$zm|kb!LAi( zn$_rzEhNkjum$Af4*OVekw8QvB^DabUyBem7^yySS5pmjCVEAy7b`o%;hvDzHvbQw z{bG6E>)6z2Mr-3t&+qC>>>&6-oIJ;=_+XiM+oCdaQJwP9+Y01~t~zpp#mPM+{iawv zCLP^LoXtsXsli+(WeGDfG99bgmJ#z%Ov5M~s8nv@8gj&c8ZSMk#Ya3QG2O&$J9?`5 zm}H4;XQUm;3+9j$oCg*zYRd*5J{L{6$Ugkz$UI7B4vBLyo~bh*Y#5m|WqP$lH(QUo zE8!5Tbpj`1NZUPsncx+HR0=^S0+-Z+92-E30)Iq-IRx4`8fQ$B$g!x+%`fLf)JvSW z6Kp6@VKR<_B@y`l&-$+iBkq=HTGW36Cm@;aS&_=iB9AwNo+i4P=wp@mMa?8r_MxX7 zU{5LmpD49$BO{$H41vT8+jdq;NzYYr-|JTH?%&A?@Ug!ZPaY_VQ!o-`Z&KUeFUXa- z#nQH&+-h!f5t!-#OgS^Ayb*UjGq;pfa z8%iU^FoV3$e0lGrI1|fBM6Tjo)Zq^`lcC!u&qU0Tzw40Hb}|G%_-l_YqA!b%EPAn+ zr@K9IFLMI3(~`4tQaFnlIUi(P%tWk|cL0gAu~qE37j+faHljm|N%!ZBbL9!iMUgEa zb+%YeJ2B@DPq=0r*(lW61nI)2FugLETiNKbGmc8s7Jw%OimW-ZQ5HdtyTOjJ(yL7d zD~j`CNvC#(bY_e2WXn*s1QHZDP$0louwM+QFVUa$H_VqNlePK(NYa+$PBb2szc_l9 z2;3$Ro4{%XFe=6lOxHJiQHTmYDwEvsr(B@6lZH6X>f6_k~mo@ZTVN1%xgAj*BR`x2=pLO zgTM;{DbyWx&Z1y$33!C5j>5FDj@p09l6Je8U_ zZAYK9RAy2p=|M4%#GU6wGNoR5BG9?$9$5bj}DwRq8UCC5x<_4dA z=y3-?YLOdgT{)A|;m~=o=Ll~uVJ6iulNzOt(ZWoMg$*Z39<0E91?!z5k=i`ev@%dy z1fSKRT~hWJSWH{^PVA3Q5FgJ!XxLW{HU|D58Wq%$aHyj(m-T0KxFgfoeo9t zt16gPy;Llk(RZRyb|Pmc#U3IP;*rym$4L>$I)bTd*#6pyzI}*k6otZ(EYHYDxg9v3 zjn4U`7K-HwDdSw!ADKt{NIND36WI?_I2SG(gV}fk`EpCJkZrLF>2ubRL;9a*Q8uTd z!1YyhmD#X-_6rv?#i~#RqF{LmoF9wHCH|{?7~68#S_SB>8Qd1b=6V7<>uH>pTu#b- z5L>atYPHCqI}LrAt$5+hEOiu&}L9t45uOi?v}<`7|&LI z0o7l_&2x>^z4Wkay84o-`&c=3ED%{bTww;&W-&K9FNaOlgTM|b!tqQ6d1ZpVazI_; z?;MoDbQ-AY185I1s1BB77CBUOKFp$!Oiu}Uo^mF~K5AL83~{@C5=w!BMlgw5E~Ji2 zVH$Ph@>>1zF#8lH!VIRtJm$e-Dz=oGE!QiV2rc;Av8Zi{B=k~I*<27*I=muZqNied zeiQwECmsI)%1sP)njmk<*QwHc(vXGp`VzYRDtLt0P_08{YeHX(LQji_K_pY9I(3?c zs+LL0A)9@SJn}XL)M_!^eieE02D*HU%$!=1FXX}%#NMi?NwKpU1wRls?hgSQMRE_- zaDsZA{$_4%*$pN-M1CfYK5fAYazH{wRNivQ2C7H`w9t_S7l?rg=u}+Rk&0=eW_E*j z;?V_CP>*K7`R7qvi$OZ2V4VsOzqmm+26Us7&YT3!7b)ga(0wg^bsyM1QQoqYK{uTT zo-cwyR>2(wPC39|xDXYRNxu{~EfvuvtMSRj1`*w{0j+`>NrZWilrFKr=|wQ^YWVd| zdSE=rd{j3jzpI;oRnb#y2DyPR|Gvf6uD(L*BMRJ_ ziYhZn@;K2yN|~5-C^1c_Fk-Xp08B1M&dVh7f(mSI2z)LBoh6%|vI>o}>PYQug~=W6 zEpaHCNlZ@*@} zD>_ZGo{FB~q8_HB98N+tjP#vIrL6*mR)a!Y$ZoWvMaRiH8-kLNLC+CNMjo1S71OZ} zzV`2($3@9VM;jJ1GEerORq(YYlzbm^QPf@%=u&~NrJ+>hv-MTT##bp-Sj&cQBX!t< z7ZL-WOhBhd!2@xbh}q1c$`IMB#)IE-KydjmsKs=w zzt^IuLtRd_Y#38MQ!R2QSEIUBPBl?^$>6k{zfCn!c~x{WQF#ZnMCMr<8-|%owQLYt zG3cy@PS!}yJOQ;jg-&LnRF7n?9iC{Sy6Ry$&7}7K+RY+WHji#rhFeidbse5)qL*d; zWtP>$SXyz0{yxP1k0Y*zr`-5YnI->0rh6xRl{)CNvu_hj$Mw1U5$QSPsjXky|Ms)QGyrL%!Cyj{!rVTO{7*+ z(VShnWIBoKNq9Xo;1}~?7>i-^rA*&)Qk2zHZav!9pPe%bP9G1e7Zcf%oih*BtB`qI zMJ3m>o6ss}@_+B01roC@2d&jJr~f&j^I+-)aP|MKbLz5dX3M@gk3E*6qcB_Zzf`;6 z=SL&9BIbA%YHyuHY+GUG``{K)^w2*mKkumSStBR<(X}q-`kzQH59Pi9R#6PED1%ot zz$yM(?+M%tnbL;qvSHnMbkPEIu3~y=*-=x!k)GNjQJxRRoyz3Tg;UIjXBW{^my@2X zptII+%SZ#8orlkW54If#U-+}n7J&Kw->W_!)wiq-4;}y&B;$P_?z)HZKm~QZ6CWtT z9Fk;xTik&^68?|{I?Sc-3WQjKr?UzsT}A(`YeNG&!HWmTuEzS(sdXQ|&EN4r*}wGQ zIGLyXD;^M7G8Z0Q3d^WK*Zq5qTh#X7tGWixyz^hU#^3RPz?o%jc%YWrZUk?(997{7 zZM`}JJvR&VnTwA&pUSQPeKx_HcS;Nz^%owJiNBGJ^IS~7u7)xH8xFNla!2A8kEe&{ zz?%P^l{C`Nec%EYRFFj%7pS0^YHq@njMT9YFEWmFgiAH&fCY-drsCF|-Sp}N^rJM` z$9VLjg0@<1hIPbHojNFV5*q9bG}scVvX%+Ik34A<%8x=NiY{Cz^Sg~yo~XHax^9-l zo<%T>DjdW*ct#WGGYZ_9fP$I^&zJ!}jqr?m`e-v6P7KU6fqX>@XfqSOk?qX@Zx+B! z1>UTMowi8-z=t-P0=^W?R8-aBnyQw)lZ|Nd7qLE9&h!L4gH+h*cxHJa*s>C2xtmN) zJib6GGu#D9ikitM)h%kKfeF4Fbu)=xD7xU`YFUo%Uk3we1~10KM+Fnj<|cr7pv1*^ zU!vcM$=xa$h%T{V1Q`~x}X9~SPTDXg@Yy^!GDoX z7GWP1u+Lgj9e+iDL-_kJ8L*HnSm$ENH_LFWD)Fppk6^*WQ#?*G6M+PCCHt&C!b-%X zj%P}bN0-k*HOyyLm&+NwRr1aRrt&0-@e14Ka(P<~#Gn8s$c~gQv0jc;{>s334a{J{ zITaN4%%gOQzgI&%o5Vwyu(`0#5>)h6Ow}sxSg(V9Hj%g42?ssUrdmX5L-_~p%RfrP zSoOC_8wL8)B}2`|XU@Z8E&%@(!&D;^w;Dz7Z&O!^)J_zAC|=IoR9s_MY8;uclx%d4 zJjqoeT((8(d2zCy(qJWrYpEPo(nK|hq(wYxc?x(i1Fa)lPSujXFpwxT4uuICVIY&* zs;XFaoQgJP8zZv|lVndR{##e^k$4evGZ}3oS5C_^7)B$U_D~=RH%Lszd?w;z{Llu; zVx#C4I^80Z+KDiTk~Rj>#LV-7`BGsHL#UPl?nOP!7^<0gyV20%C4WtaDa=4)o=?Z9 z{fAx=D;YwvXSQ zK&chAk%-zM*ylX>L5-XCyaYMV7#Z8cJgt{Ry&huFzW z!Q1=$tcgL@N|t|rH3wB|u~b6;PkU#`p2il0;S~`PD=H8O1R`R^6*S%6(3JE+AP@+| z3Pc40fk3Q4AR-VE5eNigMFb)OQGtj+L?9v%5eP&?L_`Dv5fOoixbMuKJ+PPDe{gH6 z+J|IkXTIhq_fP zm!EVAQRd8-y6d>ARr1iSv~LvGc$HG+D>%lHK5LmbHR~I@D3vRH?y+83D*JMI?xMt7 z@QM-I`(87&cjiux{!qg$cFf!vvj2InD&!5nSi&dPQ5_?Q*MtqZjS{b%A)blSh|n3r z9@fm745X`K)$#xqe?t{a;avOP(6cip_J%j~iFn2=R9SqmZ7m8pP`+>tZ=dq~pY<-j zEYH0PV-L~5V;I+irJ2$l@^pndI}R~F=qc%%T7FPalG!#Y_l8}Dg=Lv1*4x$DVz~U| z{Xa5ntE6X!x}Wfa3`(Pl!jS24lVVwK+;WyxjZq+jt>DpilOswgSJfKLcwWdkAR(O{ zr3Mice3D|F@p~>>qi;yKFoT3$t>EEVL#xVWQpmd>8Y(_g|Fux1DwW_VtCC4^O%+I1 zx~2;p!>D|%gf=vM1T~OKSSgD-hI;yBpTFq?*Qz+@knhcR7vf!{4qEhq5qmNZ>;|k1 z>D;lVGF1>*vK?8UB-B#~!LA$ph763g3T6jHz$K&fwHNGnf~y0Mv|$fM>Z44B1nP>e zm3H6n`5+Iz)PQwg8ux#v+b)mzAJL#+)c1HL|6d$*wl^s@rHo~uiO+9pW+GbT77dc9 zk3^Ry+9N=h3Jo9pCvl4F=QHIoR_C*d9ex;l9?=@{%&0+~hGd3u^~`YfujtixGc&T5 ze8$vV`oQ^d!-bFWO1 z+^=e=W(RLRvfOE&N>g$vHQTdU%A8rV>oYlcdci2!wq=?T9BP6m-%XyFMdirJMV*v5 zPgR3R^{BW3Jaa~xBKpLIIu$id2`#@&X3fT=DJ~O7sw?R~-2=}ZYZDi@?Q>$b3B??+ zSCuC1wF4~@;ddTv61P(yG0W_$kKSpH?Zv*Pt*PIM>nfq_Gq$E)aMtg?GIS*I(=wl0 zxSmPMMRPj3?>auaAW1D%RH^o;rCY;>J_@nokSSDQ4mEH+eddo;(A@YQQK~0QAW^E) zf4XEZqCLd>JfT1~xSxdh__&~|W-vXd&j4>cV`Jk*JJAQT> zoM)=2rG#h1KGR%v>zkpME?d+dX-vYzoAjx+CF8f~S*fIFa+wWNYpaw;x|V3$if+0$ z)DL&;EooP1nX&aqOS9Sc!YqqBSw_zgoT$193^@;VwhcAUndpX}+)K66DYsTdd~9-^ z6FOX3XI{fn^j>O%Gtzyh1vKcDY3Pvm@;8^#c zrpiP`Nq?B6C~NORmmR30645rE?Fw}@HlFPsLiA)D+q>Nglntm1j=n&8R8vl$)OSSh z9Gkt2JDtD#`>3(g-|9($Bi$*Eb0yDi6UXmDz}t{)YlO-JWM5;T+ftEXO?P{{!%NMd zUNxgc#|>Sl$X`dy?y_!eTtfUMzkasa(+AouOO7q1$=B>D`05k_Jq{y=?Z*kuT*yaiM}Pu~jBNd=s?a6yM>zdSF{WhOrhBq8(CODWH9c6*U0 z-B|LSz-_tdjXUZBy_>zxr<_1C)_jj>gu^^Ob^QMKHgCey*&n)~cb+dD(>3(YgnFQ7 z+VcFHC+=!%GbJXc(YFT5V1hdgQG7>ywjTUo$}BHK2Oc5#IjXqJr(5&sd^B1_`smOL zn=r=(wbVg%J)*TeocsJDgDVg5O6y-m=9|+D^nN1;=iKABS*Fv>@WELUUGcxS>SpS2 z;woIE$0yI?Lf_8XBnTD!tZ zS&_u`rC9xej%3GT`?CpkDqK|Qi-CSOPVejXv`2TU;+?akamBwI-0-z6PJeVzp5t@= L_y7Ow{RaL4d6BG% literal 0 HcmV?d00001 From a8e8f93fae6b572999a4be5c628836a6414d17cd Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 26 Nov 2021 16:10:11 +0100 Subject: [PATCH 027/492] modified prelaunch hook --- openpype/hosts/tvpaint/__init__.py | 12 ++++++++++++ openpype/hosts/tvpaint/hooks/pre_launch_args.py | 10 +++------- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/openpype/hosts/tvpaint/__init__.py b/openpype/hosts/tvpaint/__init__.py index 0e793fcf9f3..09b7c52cd1d 100644 --- a/openpype/hosts/tvpaint/__init__.py +++ b/openpype/hosts/tvpaint/__init__.py @@ -1,3 +1,6 @@ +import os + + def add_implementation_envs(env, _app): """Modify environments to contain all required for implementation.""" defaults = { @@ -6,3 +9,12 @@ def add_implementation_envs(env, _app): for key, value in defaults.items(): if not env.get(key): env[key] = value + + +def get_launch_script_path(): + current_dir = os.path.dirname(os.path.abspath(__file__)) + return os.path.join( + current_dir, + "api", + "launch_script.py" + ) diff --git a/openpype/hosts/tvpaint/hooks/pre_launch_args.py b/openpype/hosts/tvpaint/hooks/pre_launch_args.py index b0b13529ca7..62fd662d79f 100644 --- a/openpype/hosts/tvpaint/hooks/pre_launch_args.py +++ b/openpype/hosts/tvpaint/hooks/pre_launch_args.py @@ -44,10 +44,6 @@ def execute(self): self.launch_context.launch_args.extend(remainders) def launch_script_path(self): - avalon_dir = os.path.dirname(os.path.abspath(avalon.__file__)) - script_path = os.path.join( - avalon_dir, - "tvpaint", - "launch_script.py" - ) - return script_path \ No newline at end of file + from openpype.hosts.tvpaint import get_launch_script_path + + return get_launch_script_path() From 41c1f6a2d150ccdfa882b4e6a7e8003c694e1d3d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 29 Nov 2021 13:09:00 +0100 Subject: [PATCH 028/492] reuse already available variables --- openpype/lib/avalon_context.py | 13 +++++++------ .../publish/collect_anatomy_context_data.py | 16 +++++++--------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/openpype/lib/avalon_context.py b/openpype/lib/avalon_context.py index 581e4b9dbda..8fb2543412a 100644 --- a/openpype/lib/avalon_context.py +++ b/openpype/lib/avalon_context.py @@ -479,16 +479,17 @@ def get_workdir_data(project_doc, asset_doc, task_name, host_name): Returns: dict: Data prepared for filling workdir template. """ - hierarchy = "/".join(asset_doc["data"]["parents"]) - task_type = asset_doc['data']['tasks'].get(task_name, {}).get('type') project_task_types = project_doc["config"]["tasks"] task_code = project_task_types.get(task_type, {}).get("short_name") - parent = project_doc["name"] - if len(asset_doc["data"]["parents"]) != 0: - parent = asset_doc["data"]["parents"][-1] + asset_parents = asset_doc["data"]["parents"] + hierarchy = "/".join(asset_parents) + + parent_name = project_doc["name"] + if asset_parents: + parent_name = asset_parents[-1] data = { "project": { @@ -501,7 +502,7 @@ def get_workdir_data(project_doc, asset_doc, task_name, host_name): "short": task_code, }, "asset": asset_doc["name"], - "parent": parent, + "parent": parent_name, "app": host_name, "user": getpass.getuser(), "hierarchy": hierarchy, diff --git a/openpype/plugins/publish/collect_anatomy_context_data.py b/openpype/plugins/publish/collect_anatomy_context_data.py index b0c9eea5766..ae8a879fba4 100644 --- a/openpype/plugins/publish/collect_anatomy_context_data.py +++ b/openpype/plugins/publish/collect_anatomy_context_data.py @@ -49,20 +49,18 @@ def process(self, context): project_entity = context.data["projectEntity"] asset_entity = context.data["assetEntity"] - hierarchy_items = asset_entity["data"]["parents"] - hierarchy = "" - if hierarchy_items: - hierarchy = os.path.join(*hierarchy_items) - asset_tasks = asset_entity["data"]["tasks"] task_type = asset_tasks.get(task_name, {}).get("type") project_task_types = project_entity["config"]["tasks"] task_code = project_task_types.get(task_type, {}).get("short_name") - parent = project_entity["name"] - if len(asset_entity["data"]["parents"]) != 0: - parent = asset_entity["data"]["parents"][-1] + asset_parents = asset_entity["data"]["parents"] + hierarchy = "/".join(asset_parents) + + parent_name = project_entity["name"] + if asset_parents: + parent_name = asset_parents[-1] context_data = { "project": { @@ -70,7 +68,7 @@ def process(self, context): "code": project_entity["data"].get("code") }, "asset": asset_entity["name"], - "parent": parent, + "parent": parent_name, "hierarchy": hierarchy.replace("\\", "/"), "task": { "name": task_name, From 5c9a83b55dd79e9ddac6d678d4ad10cffed41bd8 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 29 Nov 2021 13:09:12 +0100 Subject: [PATCH 029/492] added missing update of parent --- openpype/plugins/publish/collect_anatomy_instance_data.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openpype/plugins/publish/collect_anatomy_instance_data.py b/openpype/plugins/publish/collect_anatomy_instance_data.py index da6a2195eeb..74b556e28ab 100644 --- a/openpype/plugins/publish/collect_anatomy_instance_data.py +++ b/openpype/plugins/publish/collect_anatomy_instance_data.py @@ -242,7 +242,11 @@ def fill_anatomy_data(self, context): asset_doc = instance.data.get("assetEntity") if asset_doc and asset_doc["_id"] != context_asset_doc["_id"]: parents = asset_doc["data"].get("parents") or list() + parent_name = project_doc["name"] + if parents: + parent_name = parents[-1] anatomy_updates["hierarchy"] = "/".join(parents) + anatomy_updates["parent"] = parent_name # Task task_name = instance.data.get("task") From 853ba0efb37a464b7ee2db0781a43ffee9a132d1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 29 Nov 2021 13:10:06 +0100 Subject: [PATCH 030/492] removed conflict marks from docstring --- website/docs/admin_settings_project_anatomy.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/website/docs/admin_settings_project_anatomy.md b/website/docs/admin_settings_project_anatomy.md index e897b0ffab9..5cb396264e5 100644 --- a/website/docs/admin_settings_project_anatomy.md +++ b/website/docs/admin_settings_project_anatomy.md @@ -57,14 +57,11 @@ We have a few required anatomy templates for OpenPype to work properly, however | `project[code]` | Project's code | | `hierarchy` | All hierarchical parents as subfolders | | `asset` | Name of asset or shot | -<<<<<<< HEAD | `task[name]` | Name of task | | `task[type]` | Type of task | | `task[short]` | Shortname of task | -======= | `parent` | Name of parent folder | | `task` | Name of task | ->>>>>>> add7db0c0... add parent asset to doc | `version` | Version number | | `subset` | Subset name | | `family` | Main family name | From c6916ef96cd7470a4e24668c0d6b5951363c7149 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 29 Nov 2021 13:11:17 +0100 Subject: [PATCH 031/492] modified description of parent --- website/docs/admin_settings_project_anatomy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/admin_settings_project_anatomy.md b/website/docs/admin_settings_project_anatomy.md index 5cb396264e5..a8be77d25bf 100644 --- a/website/docs/admin_settings_project_anatomy.md +++ b/website/docs/admin_settings_project_anatomy.md @@ -60,7 +60,7 @@ We have a few required anatomy templates for OpenPype to work properly, however | `task[name]` | Name of task | | `task[type]` | Type of task | | `task[short]` | Shortname of task | -| `parent` | Name of parent folder | +| `parent` | Name of hierarchical parent | | `task` | Name of task | | `version` | Version number | | `subset` | Subset name | From 741f326935f73a45f6b15da8185ee3d2c3c7ba70 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 29 Nov 2021 13:13:01 +0100 Subject: [PATCH 032/492] removed not needed replacement --- openpype/plugins/publish/collect_anatomy_context_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/collect_anatomy_context_data.py b/openpype/plugins/publish/collect_anatomy_context_data.py index ae8a879fba4..07de1b4420e 100644 --- a/openpype/plugins/publish/collect_anatomy_context_data.py +++ b/openpype/plugins/publish/collect_anatomy_context_data.py @@ -69,7 +69,7 @@ def process(self, context): }, "asset": asset_entity["name"], "parent": parent_name, - "hierarchy": hierarchy.replace("\\", "/"), + "hierarchy": hierarchy, "task": { "name": task_name, "type": task_type, From 77cdd406108f7fa0b4506153d196f8f237a96946 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 29 Nov 2021 13:16:24 +0100 Subject: [PATCH 033/492] changed variable name in workfiles tool --- openpype/tools/workfiles/app.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/openpype/tools/workfiles/app.py b/openpype/tools/workfiles/app.py index 4253f7450a1..987b886864a 100644 --- a/openpype/tools/workfiles/app.py +++ b/openpype/tools/workfiles/app.py @@ -85,9 +85,10 @@ def __init__(self, parent, root, anatomy, template_key, session=None): project_task_types = project_doc["config"]["tasks"] task_short = project_task_types.get(task_type, {}).get("short_name") - parent = project_doc["name"] - if len(asset_doc["data"]["parents"]) != 0: - parent = asset_doc["data"]["parents"][-1] + asset_parents = asset_doc["data"]["parents"] + parent_name = project_doc["name"] + if asset_parents: + parent_name = asset_parents[-1] self.data = { "project": { @@ -100,7 +101,7 @@ def __init__(self, parent, root, anatomy, template_key, session=None): "type": task_type, "short": task_short, }, - "parent": parent, + "parent": parent_name, "version": 1, "user": getpass.getuser(), "comment": "", From 5795636af55067be12e3992408d40c23c339c1a4 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 30 Nov 2021 14:39:43 +0100 Subject: [PATCH 034/492] OP-2042 - updates to db dumps and loads --- tests/lib/db_handler.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/tests/lib/db_handler.py b/tests/lib/db_handler.py index 9be70895da6..4dde5ba46ef 100644 --- a/tests/lib/db_handler.py +++ b/tests/lib/db_handler.py @@ -136,7 +136,8 @@ def teardown(self, db_name): print("Dropping {} database".format(db_name)) self.client.drop_database(db_name) - def backup_to_dump(self, db_name, dump_dir, overwrite=False): + def backup_to_dump(self, db_name, dump_dir, overwrite=False, + collection=None): """ Helper method for running mongodump for specific 'db_name' """ @@ -148,7 +149,8 @@ def backup_to_dump(self, db_name, dump_dir, overwrite=False): raise RuntimeError("Backup already exists, " "run with overwrite=True") - query = self._dump_query(self.uri, dump_dir, db_name=db_name) + query = self._dump_query(self.uri, dump_dir, + db_name=db_name, collection=collection) print("Mongodump query:: {}".format(query)) subprocess.run(query) @@ -187,7 +189,8 @@ def _restore_query(self, uri, dump_dir, drop_part = "--drop" if db_name_out: - db_part += " --nsTo={}.*".format(db_name_out) + collection_str = collection or '*' + db_part += " --nsTo={}.{}".format(db_name_out, collection_str) query = "\"{}\" --uri=\"{}\" --dir=\"{}\" {} {} {}".format( "mongorestore", uri, dump_dir, db_part, coll_part, drop_part @@ -217,12 +220,12 @@ def _import_query(self, uri, sql_url, return query -# handler = DBHandler(uri="mongodb://localhost:27017") +#handler = DBHandler(uri="mongodb://localhost:27017") # -# backup_dir = "c:\\projects\\dumps" +#backup_dir = "c:\\projects\\test_nuke_publish\\input\\dumps" # # -# handler.backup_to_dump("openpype", backup_dir, True) -# # handler.setup_from_dump("test_db", backup_dir, True) +#handler.backup_to_dump("avalon", backup_dir, True, collection="test_project") +#handler.setup_from_dump("test_db", backup_dir, True, db_name_out="avalon", collection="test_project") # # handler.setup_from_sql_file("test_db", "c:\\projects\\sql\\item.sql", # # collection="test_project", # # drop=False, mode="upsert") From 3fa0b39df196524ad4dd8b013e90ebe02a59e1b6 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 30 Nov 2021 14:40:01 +0100 Subject: [PATCH 035/492] OP-2042 - wip testing in Nuke --- .../hosts/nuke/test_publish_in_nuke.py | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 tests/integration/hosts/nuke/test_publish_in_nuke.py diff --git a/tests/integration/hosts/nuke/test_publish_in_nuke.py b/tests/integration/hosts/nuke/test_publish_in_nuke.py new file mode 100644 index 00000000000..3f3f191ac7e --- /dev/null +++ b/tests/integration/hosts/nuke/test_publish_in_nuke.py @@ -0,0 +1,94 @@ +import pytest +import os +import shutil + +from tests.lib.testing_classes import PublishTest + + +class TestPublishInNuke(PublishTest): + """Basic test case for publishing in Nuke + + Uses generic TestCase to prepare fixtures for test data, testing DBs, + env vars. + + Opens Maya, run publish on prepared workile. + + Then checks content of DB (if subset, version, representations were + created. + Checks tmp folder if all expected files were published. + + """ + PERSIST = True + + TEST_FILES = [ + ("1Bciy2pCwMKl1UIpxuPnlX_LHMo_Xkq0K", "test_Nuke_publish.zip", "") + ] + + APP = "Nuke" + APP_VARIANT = "12" + + APP_NAME = "{}/{}".format(APP, APP_VARIANT) + + TIMEOUT = 120 # publish timeout + + @pytest.fixture(scope="module") + def last_workfile_path(self, download_test_data): + """Get last_workfile_path from source data. + + Maya expects workfile in proper folder, so copy is done first. + """ + src_path = os.path.join(download_test_data, + "input", + "workfile", + "test_project_test_asset_TestTask_v001.psd") + dest_folder = os.path.join(download_test_data, + self.PROJECT, + self.ASSET, + "work", + self.TASK) + os.makedirs(dest_folder) + dest_path = os.path.join(dest_folder, + "test_project_test_asset_TestTask_v001.psd") + shutil.copy(src_path, dest_path) + + yield dest_path + + @pytest.fixture(scope="module") + def startup_scripts(self, monkeypatch_session, download_test_data): + """Points Maya to userSetup file from input data""" + pass + + def test_db_asserts(self, dbcon, publish_finished): + """Host and input data dependent expected results in DB.""" + print("test_db_asserts") + assert 5 == dbcon.count_documents({"type": "version"}), \ + "Not expected no of versions" + + assert 0 == dbcon.count_documents({"type": "version", + "name": {"$ne": 1}}), \ + "Only versions with 1 expected" + + assert 1 == dbcon.count_documents({"type": "subset", + "name": "modelMain"}), \ + "modelMain subset must be present" + + assert 1 == dbcon.count_documents({"type": "subset", + "name": "workfileTest_task"}), \ + "workfileTest_task subset must be present" + + assert 11 == dbcon.count_documents({"type": "representation"}), \ + "Not expected no of representations" + + assert 2 == dbcon.count_documents({"type": "representation", + "context.subset": "modelMain", + "context.ext": "abc"}), \ + "Not expected no of representations with ext 'abc'" + + assert 2 == dbcon.count_documents({"type": "representation", + "context.subset": "modelMain", + "context.ext": "ma"}), \ + "Not expected no of representations with ext 'abc'" + + +if __name__ == "__main__": + test_case = TestPublishInNuke() From 9576b823ca4bd3f4573fa14c652eb421a15747bd Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Wed, 1 Dec 2021 11:29:12 +0100 Subject: [PATCH 036/492] fix hda creation on single object --- .../houdini/plugins/create/create_hda.py | 28 ++++++++----------- repos/avalon-core | 2 +- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/openpype/hosts/houdini/plugins/create/create_hda.py b/openpype/hosts/houdini/plugins/create/create_hda.py index 2af1e4a2579..96b2cfe6fc8 100644 --- a/openpype/hosts/houdini/plugins/create/create_hda.py +++ b/openpype/hosts/houdini/plugins/create/create_hda.py @@ -45,19 +45,14 @@ def _process(self, instance): if (self.options or {}).get("useSelection") and self.nodes: # if we have `use selection` enabled and we have some # selected nodes ... - to_hda = self.nodes[0] - if len(self.nodes) > 1: - # if there is more then one node, create subnet first - subnet = out.createNode( - "subnet", node_name="{}_subnet".format(self.name)) - to_hda = subnet - else: - # in case of no selection, just create subnet node - subnet = out.createNode( - "subnet", node_name="{}_subnet".format(self.name)) + subnet = out.collapseIntoSubnet( + self.nodes, + subnet_name="{}_subnet".format(self.name)) subnet.moveToGoodPosition() to_hda = subnet - + else: + to_hda = out.createNode( + "subnet", node_name="{}_subnet".format(self.name)) if not to_hda.type().definition(): # if node type has not its definition, it is not user # created hda. We test if hda can be created from the node. @@ -69,13 +64,14 @@ def _process(self, instance): name=subset_name, hda_file_name="$HIP/{}.hda".format(subset_name) ) - hou.moveNodesTo(self.nodes, hda_node) + # print("move to hda: {}".format(to_hda)) + # hou.moveNodesTo([to_hda], hda_node) hda_node.layoutChildren() + elif self._check_existing(subset_name): + raise plugin.OpenPypeCreatorError( + ("subset {} is already published with different HDA" + "definition.").format(subset_name)) else: - if self._check_existing(subset_name): - raise plugin.OpenPypeCreatorError( - ("subset {} is already published with different HDA" - "definition.").format(subset_name)) hda_node = to_hda hda_node.setName(subset_name) diff --git a/repos/avalon-core b/repos/avalon-core index 7e5efd68853..9499f6517a1 160000 --- a/repos/avalon-core +++ b/repos/avalon-core @@ -1 +1 @@ -Subproject commit 7e5efd6885330d84bb8495975bcab84df49bfa3d +Subproject commit 9499f6517a1ff2d3bf94c5d34c0aece146734760 From 6d309c0a28c58e629ab2064cbe6792c07949a5f2 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Wed, 1 Dec 2021 11:29:46 +0100 Subject: [PATCH 037/492] remove debug prints --- openpype/hosts/houdini/plugins/create/create_hda.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openpype/hosts/houdini/plugins/create/create_hda.py b/openpype/hosts/houdini/plugins/create/create_hda.py index 96b2cfe6fc8..afb0797e047 100644 --- a/openpype/hosts/houdini/plugins/create/create_hda.py +++ b/openpype/hosts/houdini/plugins/create/create_hda.py @@ -64,8 +64,6 @@ def _process(self, instance): name=subset_name, hda_file_name="$HIP/{}.hda".format(subset_name) ) - # print("move to hda: {}".format(to_hda)) - # hou.moveNodesTo([to_hda], hda_node) hda_node.layoutChildren() elif self._check_existing(subset_name): raise plugin.OpenPypeCreatorError( From edefd8bff197bc6d30bf43040d729af10cb649f3 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Wed, 1 Dec 2021 11:32:35 +0100 Subject: [PATCH 038/492] =?UTF-8?q?fix=20hound=20=F0=9F=90=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- openpype/hosts/houdini/plugins/create/create_hda.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/houdini/plugins/create/create_hda.py b/openpype/hosts/houdini/plugins/create/create_hda.py index afb0797e047..459da8bfdfb 100644 --- a/openpype/hosts/houdini/plugins/create/create_hda.py +++ b/openpype/hosts/houdini/plugins/create/create_hda.py @@ -52,7 +52,7 @@ def _process(self, instance): to_hda = subnet else: to_hda = out.createNode( - "subnet", node_name="{}_subnet".format(self.name)) + "subnet", node_name="{}_subnet".format(self.name)) if not to_hda.type().definition(): # if node type has not its definition, it is not user # created hda. We test if hda can be created from the node. From 64c4e5f2bbae767e6f2e126c884853a1fab39eaf Mon Sep 17 00:00:00 2001 From: "clement.hector" Date: Wed, 1 Dec 2021 16:42:18 +0100 Subject: [PATCH 039/492] Add static and allView option in imagePlaneLoader --- .../maya/plugins/load/load_image_plane.py | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_image_plane.py b/openpype/hosts/maya/plugins/load/load_image_plane.py index f2640dc2ebe..f7a5a7ea18f 100644 --- a/openpype/hosts/maya/plugins/load/load_image_plane.py +++ b/openpype/hosts/maya/plugins/load/load_image_plane.py @@ -13,10 +13,14 @@ def __init__(self, cameras): self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint) self.camera = None + self.static_image_plane = False + self.show_in_all_views = False self.widgets = { "label": QtWidgets.QLabel("Select camera for image plane."), "list": QtWidgets.QListWidget(), + "staticImagePlane": QtWidgets.QCheckBox(), + "showInAllViews": QtWidgets.QCheckBox(), "warning": QtWidgets.QLabel("No cameras selected!"), "buttons": QtWidgets.QWidget(), "okButton": QtWidgets.QPushButton("Ok"), @@ -31,6 +35,9 @@ def __init__(self, cameras): for camera in cameras: self.widgets["list"].addItem(camera) + self.widgets["staticImagePlane"].setText("Make Image Plane Static") + self.widgets["showInAllViews"].setText("Show Image Plane in All Views") + # Build buttons. layout = QtWidgets.QHBoxLayout(self.widgets["buttons"]) layout.addWidget(self.widgets["okButton"]) @@ -40,6 +47,8 @@ def __init__(self, cameras): layout = QtWidgets.QVBoxLayout(self) layout.addWidget(self.widgets["label"]) layout.addWidget(self.widgets["list"]) + layout.addWidget(self.widgets["staticImagePlane"]) + layout.addWidget(self.widgets["showInAllViews"]) layout.addWidget(self.widgets["buttons"]) layout.addWidget(self.widgets["warning"]) @@ -54,6 +63,8 @@ def on_ok_pressed(self): if self.camera is None: self.widgets["warning"].setVisible(True) return + self.show_in_all_views = self.widgets["showInAllViews"].isChecked() + self.static_image_plane = self.widgets["staticImagePlane"].isChecked() self.close() @@ -65,15 +76,15 @@ def on_cancel_pressed(self): class ImagePlaneLoader(api.Loader): """Specific loader of plate for image planes on selected camera.""" - families = ["plate", "render"] + families = ["image", "plate", "render"] label = "Load imagePlane." representations = ["mov", "exr", "preview", "png"] icon = "image" color = "orange" - def load(self, context, name, namespace, data): + def load(self, context, name, namespace, data, option=None): import pymel.core as pm - + new_nodes = [] image_plane_depth = 1000 asset = context['asset']['name'] @@ -85,18 +96,16 @@ def load(self, context, name, namespace, data): # Get camera from user selection. camera = None - default_cameras = [ - "frontShape", "perspShape", "sideShape", "topShape" - ] - cameras = [ - x for x in pm.ls(type="camera") if x.name() not in default_cameras - ] + cameras = pm.ls(type="camera") camera_names = {x.getParent().name(): x for x in cameras} camera_names["Create new camera."] = "create_camera" window = CameraWindow(camera_names.keys()) window.exec_() camera = camera_names[window.camera] + is_static_image_plane = window.static_image_plane + is_in_all_views = window.show_in_all_views + if camera == "create_camera": camera = pm.createNode("camera") @@ -111,7 +120,7 @@ def load(self, context, name, namespace, data): # Create image plane image_plane_transform, image_plane_shape = pm.imagePlane( - camera=camera, showInAllViews=False + camera=camera, showInAllViews=is_in_all_views ) image_plane_shape.depth.set(image_plane_depth) @@ -119,6 +128,9 @@ def load(self, context, name, namespace, data): context["representation"]["data"]["path"] ) + if is_static_image_plane: + image_plane_shape.detach() + start_frame = pm.playbackOptions(q=True, min=True) end_frame = pm.playbackOptions(q=True, max=True) From d48fdaeceb3af83131b009c748145a6678367f60 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 1 Dec 2021 18:29:56 +0100 Subject: [PATCH 040/492] Fix - missing extension for new workfile --- openpype/hooks/pre_foundry_apps.py | 2 +- repos/avalon-core | 2 +- .../hosts/nuke/test_publish_in_nuke.py | 41 ++++++++++++++----- tests/lib/db_handler.py | 8 ++-- 4 files changed, 36 insertions(+), 17 deletions(-) diff --git a/openpype/hooks/pre_foundry_apps.py b/openpype/hooks/pre_foundry_apps.py index 85f68c6b604..70554cbedbf 100644 --- a/openpype/hooks/pre_foundry_apps.py +++ b/openpype/hooks/pre_foundry_apps.py @@ -13,7 +13,7 @@ class LaunchFoundryAppsWindows(PreLaunchHook): # Should be as last hook because must change launch arguments to string order = 1000 - app_groups = ["nuke", "nukex", "hiero", "nukestudio"] + app_groups = ["nuke", "nukex", "hiero", "nukestudio", "aftereffects"] platforms = ["windows"] def execute(self): diff --git a/repos/avalon-core b/repos/avalon-core index 7e5efd68853..e37f4f92ed2 160000 --- a/repos/avalon-core +++ b/repos/avalon-core @@ -1 +1 @@ -Subproject commit 7e5efd6885330d84bb8495975bcab84df49bfa3d +Subproject commit e37f4f92ed25f89c870fdcb7f9538da7d0d7de90 diff --git a/tests/integration/hosts/nuke/test_publish_in_nuke.py b/tests/integration/hosts/nuke/test_publish_in_nuke.py index 3f3f191ac7e..abadb0fb924 100644 --- a/tests/integration/hosts/nuke/test_publish_in_nuke.py +++ b/tests/integration/hosts/nuke/test_publish_in_nuke.py @@ -1,9 +1,12 @@ import pytest import os import shutil +import logging from tests.lib.testing_classes import PublishTest +log = logging.getLogger("test_publish_in_nuke") + class TestPublishInNuke(PublishTest): """Basic test case for publishing in Nuke @@ -21,11 +24,11 @@ class TestPublishInNuke(PublishTest): PERSIST = True TEST_FILES = [ - ("1Bciy2pCwMKl1UIpxuPnlX_LHMo_Xkq0K", "test_Nuke_publish.zip", "") + ("1635L4gww9nEkP-1EclfWXNdeDuRjDhey", "test_Nuke_publish.zip", "") ] - APP = "Nuke" - APP_VARIANT = "12" + APP = "nuke" + APP_VARIANT = "12-2" APP_NAME = "{}/{}".format(APP, APP_VARIANT) @@ -37,26 +40,42 @@ def last_workfile_path(self, download_test_data): Maya expects workfile in proper folder, so copy is done first. """ - src_path = os.path.join(download_test_data, - "input", - "workfile", - "test_project_test_asset_TestTask_v001.psd") + print("last_workfile_path") + log.info("log last_workfile_path") + src_path = os.path.join( + download_test_data, + "input", + "workfile", + "test_project_test_asset_CompositingInNuke_v001.nk") dest_folder = os.path.join(download_test_data, self.PROJECT, self.ASSET, "work", self.TASK) os.makedirs(dest_folder) - dest_path = os.path.join(dest_folder, - "test_project_test_asset_TestTask_v001.psd") + dest_path = os.path.join( + dest_folder, "test_project_test_asset_CompositingInNuke_v001.nk") shutil.copy(src_path, dest_path) yield dest_path @pytest.fixture(scope="module") def startup_scripts(self, monkeypatch_session, download_test_data): - """Points Maya to userSetup file from input data""" - pass + """Points Nuke to userSetup file from input data""" + print("startup_scripts") + log.info("log startup_scripts") + startup_path = os.path.join(download_test_data, + "input", + "startup") + startup_path = "C:\\projects\\test_nuke_publish\\input\\startup" + original_pythonpath = os.environ.get("NUKE_PATH") + monkeypatch_session.setenv("NUKE_PATH", + "{}{}{}".format(original_pythonpath, + os.pathsep, + startup_path)) + print("NUKE_PATH:: {}{}{}".format(startup_path, + os.pathsep, + original_pythonpath)) def test_db_asserts(self, dbcon, publish_finished): """Host and input data dependent expected results in DB.""" diff --git a/tests/lib/db_handler.py b/tests/lib/db_handler.py index 4dde5ba46ef..88cde4d05f0 100644 --- a/tests/lib/db_handler.py +++ b/tests/lib/db_handler.py @@ -165,7 +165,7 @@ def _dump_query(self, uri, output_path, db_name=None, collection=None): if collection: if not db_name: raise ValueError("db_name must be present") - coll_part = "--nsInclude={}.{}".format(db_name, collection) + coll_part = "--collection={}".format(collection) query = "\"{}\" --uri=\"{}\" --out={} {} {}".format( "mongodump", uri, output_path, db_part, coll_part ) @@ -220,11 +220,11 @@ def _import_query(self, uri, sql_url, return query -#handler = DBHandler(uri="mongodb://localhost:27017") +handler = DBHandler(uri="mongodb://localhost:27017") # -#backup_dir = "c:\\projects\\test_nuke_publish\\input\\dumps" +backup_dir = "c:\\projects\\test_nuke_publish\\input\\dumps" # # -#handler.backup_to_dump("avalon", backup_dir, True, collection="test_project") +handler.backup_to_dump("avalon", backup_dir, True, collection="test_project") #handler.setup_from_dump("test_db", backup_dir, True, db_name_out="avalon", collection="test_project") # # handler.setup_from_sql_file("test_db", "c:\\projects\\sql\\item.sql", # # collection="test_project", From 3d6bf6e8c5eb5e8774a231fa6ff77e1422cf26af Mon Sep 17 00:00:00 2001 From: "clement.hector" Date: Wed, 1 Dec 2021 18:56:38 +0100 Subject: [PATCH 041/492] change option by options --- openpype/hosts/maya/plugins/load/load_image_plane.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/load/load_image_plane.py b/openpype/hosts/maya/plugins/load/load_image_plane.py index f7a5a7ea18f..d7e61b9c642 100644 --- a/openpype/hosts/maya/plugins/load/load_image_plane.py +++ b/openpype/hosts/maya/plugins/load/load_image_plane.py @@ -82,7 +82,7 @@ class ImagePlaneLoader(api.Loader): icon = "image" color = "orange" - def load(self, context, name, namespace, data, option=None): + def load(self, context, name, namespace, data, options=None): import pymel.core as pm new_nodes = [] From f91045d497f0a2a509feb3afdc04fec60027ae31 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 2 Dec 2021 11:33:07 +0100 Subject: [PATCH 042/492] small fixes --- openpype/hosts/tvpaint/api/launch_script.py | 2 +- openpype/hosts/tvpaint/api/lib.py | 4 ---- openpype/hosts/tvpaint/api/pipeline.py | 17 ++++++++++------- .../plugins/publish/collect_instances.py | 2 +- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/openpype/hosts/tvpaint/api/launch_script.py b/openpype/hosts/tvpaint/api/launch_script.py index ec34442d28e..e66bf61df65 100644 --- a/openpype/hosts/tvpaint/api/launch_script.py +++ b/openpype/hosts/tvpaint/api/launch_script.py @@ -35,7 +35,7 @@ def main(launch_args): # Create Communicator object and trigger launch # - this must be done before anything is processed - communicator = CommunicationWrapper.create_communicator(qt_app) + communicator = CommunicationWrapper.create_qt_communicator(qt_app) communicator.launch(launch_args) def process_in_main_thread(): diff --git a/openpype/hosts/tvpaint/api/lib.py b/openpype/hosts/tvpaint/api/lib.py index e41bbb5aadf..654aff19d8e 100644 --- a/openpype/hosts/tvpaint/api/lib.py +++ b/openpype/hosts/tvpaint/api/lib.py @@ -5,10 +5,6 @@ import avalon.io from . import CommunicationWrapper -from .pipeline import ( - list_instances, - write_instances -) log = logging.getLogger(__name__) diff --git a/openpype/hosts/tvpaint/api/pipeline.py b/openpype/hosts/tvpaint/api/pipeline.py index 235d5525f49..e7c5159bbc4 100644 --- a/openpype/hosts/tvpaint/api/pipeline.py +++ b/openpype/hosts/tvpaint/api/pipeline.py @@ -15,7 +15,10 @@ from openpype.hosts import tvpaint from openpype.api import get_current_project_settings -from .communication_server import CommunicationWrapper +from .lib import ( + execute_george, + execute_george_through_file +) log = logging.getLogger(__name__) @@ -214,7 +217,7 @@ def get_workfile_metadata_string_for_keys(metadata_keys): # Execute the script george_script = "\n".join(george_script_parts) - CommunicationWrapper.execute_george_through_file(george_script) + execute_george_through_file(george_script) # Load data from temp file with open(output_filepath, "r") as stream: @@ -329,7 +332,7 @@ def write_workfile_metadata(metadata_key, value): george_script = "\n".join(george_script_parts) - return CommunicationWrapper.execute_george_through_file(george_script) + return execute_george_through_file(george_script) def get_current_workfile_context(): @@ -443,7 +446,7 @@ def set_context_settings(asset_doc=None): framerate = project_doc["data"].get("fps") if framerate is not None: - CommunicationWrapper.execute_george( + execute_george( "tv_framerate {} \"timestretch\"".format(framerate) ) else: @@ -461,7 +464,7 @@ def set_context_settings(asset_doc=None): if width is None or height is None: print("Resolution was not found!") else: - CommunicationWrapper.execute_george( + execute_george( "tv_resizepage {} {} 0".format(width, height) ) @@ -484,5 +487,5 @@ def set_context_settings(asset_doc=None): mark_in = 0 mark_out = mark_in + (frame_end - frame_start) + handle_start + handle_end - CommunicationWrapper.execute_george("tv_markin {} set".format(mark_in)) - CommunicationWrapper.execute_george("tv_markout {} set".format(mark_out)) + execute_george("tv_markin {} set".format(mark_in)) + execute_george("tv_markout {} set".format(mark_out)) diff --git a/openpype/hosts/tvpaint/plugins/publish/collect_instances.py b/openpype/hosts/tvpaint/plugins/publish/collect_instances.py index 1d7a48e389e..31d2fd1fd56 100644 --- a/openpype/hosts/tvpaint/plugins/publish/collect_instances.py +++ b/openpype/hosts/tvpaint/plugins/publish/collect_instances.py @@ -218,7 +218,7 @@ def create_render_pass_instance(self, context, instance_data): # - not 100% working as it was found out that layer ids can't be # used as unified identifier across multiple workstations layers_by_id = { - layer["id"]: layer + layer["layer_id"]: layer for layer in layers_data } layer_ids = instance_data["layer_ids"] From e0096b8c6b866337b01800e168a8f636201f54f0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 2 Dec 2021 11:50:18 +0100 Subject: [PATCH 043/492] removed unused loc file --- openpype/hosts/tvpaint/resources/avalon.loc | 37 --------------------- 1 file changed, 37 deletions(-) delete mode 100644 openpype/hosts/tvpaint/resources/avalon.loc diff --git a/openpype/hosts/tvpaint/resources/avalon.loc b/openpype/hosts/tvpaint/resources/avalon.loc deleted file mode 100644 index 3cfb7e9db4c..00000000000 --- a/openpype/hosts/tvpaint/resources/avalon.loc +++ /dev/null @@ -1,37 +0,0 @@ -#------------------------------------------------- -#------------ AVALON PLUGIN LOC FILE ------------- -#------------------------------------------------- - -#Language : English -#Version : 1.0 -#Date : 27/10/2020 - -#------------------------------------------------- -#------------ COMMON ----------------------------- -#------------------------------------------------- - -$100 "OpenPype Tools" - -$10010 "Workfiles" -$10020 "Load" -$10030 "Create" -$10040 "Scene inventory" -$10050 "Publish" -$10060 "Library" - -#------------ Help ------------------------------- - -$20010 "Open workfiles tool" -$20020 "Open loader tool" -$20030 "Open creator tool" -$20040 "Open scene inventory tool" -$20050 "Open publisher" -$20060 "Open library loader tool" - -#------------ Errors ----------------------------- - -$30001 "Can't Open Requester !" - -#------------------------------------------------- -#------------ END -------------------------------- -#------------------------------------------------- From 3c7b622b6e3bf56deb28b12138f980b73a4ef6d6 Mon Sep 17 00:00:00 2001 From: "clement.hector" Date: Thu, 2 Dec 2021 11:53:32 +0100 Subject: [PATCH 044/492] fix rotation after detach --- .../maya/plugins/load/load_image_plane.py | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_image_plane.py b/openpype/hosts/maya/plugins/load/load_image_plane.py index d7e61b9c642..ecfa8d7bc0a 100644 --- a/openpype/hosts/maya/plugins/load/load_image_plane.py +++ b/openpype/hosts/maya/plugins/load/load_image_plane.py @@ -96,15 +96,23 @@ def load(self, context, name, namespace, data, options=None): # Get camera from user selection. camera = None - cameras = pm.ls(type="camera") - camera_names = {x.getParent().name(): x for x in cameras} - camera_names["Create new camera."] = "create_camera" - window = CameraWindow(camera_names.keys()) - window.exec_() - camera = camera_names[window.camera] - - is_static_image_plane = window.static_image_plane - is_in_all_views = window.show_in_all_views + is_static_image_plane = None + is_in_all_views = None + if data: + camera = pm.PyNode(data.get("camera")) + is_static_image_plane = data.get("static_image_plane") + is_in_all_views = data.get("in_all_views") + + if not camera: + cameras = pm.ls(type="camera") + camera_names = {x.getParent().name(): x for x in cameras} + camera_names["Create new camera."] = "create_camera" + window = CameraWindow(camera_names.keys()) + window.exec_() + camera = camera_names[window.camera] + + is_static_image_plane = window.static_image_plane + is_in_all_views = window.show_in_all_views if camera == "create_camera": camera = pm.createNode("camera") @@ -129,7 +137,9 @@ def load(self, context, name, namespace, data, options=None): ) if is_static_image_plane: + image_plane_shape.setMaintainRatio(True) image_plane_shape.detach() + image_plane_transform.setRotation(camera.getRotation()) start_frame = pm.playbackOptions(q=True, min=True) end_frame = pm.playbackOptions(q=True, max=True) From 931896519c05a423f4630001372bdb72d47da847 Mon Sep 17 00:00:00 2001 From: "clement.hector" Date: Thu, 2 Dec 2021 12:34:34 +0100 Subject: [PATCH 045/492] add fileName in imagePlane cmd --- openpype/hosts/maya/plugins/load/load_image_plane.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_image_plane.py b/openpype/hosts/maya/plugins/load/load_image_plane.py index ecfa8d7bc0a..eea5844e8b1 100644 --- a/openpype/hosts/maya/plugins/load/load_image_plane.py +++ b/openpype/hosts/maya/plugins/load/load_image_plane.py @@ -128,16 +128,12 @@ def load(self, context, name, namespace, data, options=None): # Create image plane image_plane_transform, image_plane_shape = pm.imagePlane( + fileName=context["representation"]["data"]["path"], camera=camera, showInAllViews=is_in_all_views ) image_plane_shape.depth.set(image_plane_depth) - image_plane_shape.imageName.set( - context["representation"]["data"]["path"] - ) - if is_static_image_plane: - image_plane_shape.setMaintainRatio(True) image_plane_shape.detach() image_plane_transform.setRotation(camera.getRotation()) From d27b11774888d1af6dac39c25384bca25c900413 Mon Sep 17 00:00:00 2001 From: "clement.hector" Date: Thu, 2 Dec 2021 13:52:59 +0100 Subject: [PATCH 046/492] remove context key task from doc --- website/docs/admin_settings_project_anatomy.md | 1 - 1 file changed, 1 deletion(-) diff --git a/website/docs/admin_settings_project_anatomy.md b/website/docs/admin_settings_project_anatomy.md index a8be77d25bf..1f742c31ed0 100644 --- a/website/docs/admin_settings_project_anatomy.md +++ b/website/docs/admin_settings_project_anatomy.md @@ -61,7 +61,6 @@ We have a few required anatomy templates for OpenPype to work properly, however | `task[type]` | Type of task | | `task[short]` | Shortname of task | | `parent` | Name of hierarchical parent | -| `task` | Name of task | | `version` | Version number | | `subset` | Subset name | | `family` | Main family name | From 991328c516a16c206bf5abacc58738a95aba9b11 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 2 Dec 2021 16:29:26 +0100 Subject: [PATCH 047/492] moved clockify module --- openpype/modules/base.py | 1 + openpype/modules/{default_modules => }/clockify/__init__.py | 0 openpype/modules/{default_modules => }/clockify/clockify_api.py | 0 .../modules/{default_modules => }/clockify/clockify_module.py | 0 openpype/modules/{default_modules => }/clockify/constants.py | 0 .../clockify/ftrack/server/action_clockify_sync_server.py | 0 .../clockify/ftrack/user/action_clockify_sync_local.py | 0 .../clockify/launcher_actions/ClockifyStart.py | 0 .../clockify/launcher_actions/ClockifySync.py | 0 openpype/modules/{default_modules => }/clockify/widgets.py | 0 10 files changed, 1 insertion(+) rename openpype/modules/{default_modules => }/clockify/__init__.py (100%) rename openpype/modules/{default_modules => }/clockify/clockify_api.py (100%) rename openpype/modules/{default_modules => }/clockify/clockify_module.py (100%) rename openpype/modules/{default_modules => }/clockify/constants.py (100%) rename openpype/modules/{default_modules => }/clockify/ftrack/server/action_clockify_sync_server.py (100%) rename openpype/modules/{default_modules => }/clockify/ftrack/user/action_clockify_sync_local.py (100%) rename openpype/modules/{default_modules => }/clockify/launcher_actions/ClockifyStart.py (100%) rename openpype/modules/{default_modules => }/clockify/launcher_actions/ClockifySync.py (100%) rename openpype/modules/{default_modules => }/clockify/widgets.py (100%) diff --git a/openpype/modules/base.py b/openpype/modules/base.py index 7ecfeae7bd5..fdca9e90be4 100644 --- a/openpype/modules/base.py +++ b/openpype/modules/base.py @@ -273,6 +273,7 @@ def _load_modules(): # Import default modules imported from 'openpype.modules' for default_module_name in ( + "clockify", "settings_action", "launcher_action", "project_manager_action", diff --git a/openpype/modules/default_modules/clockify/__init__.py b/openpype/modules/clockify/__init__.py similarity index 100% rename from openpype/modules/default_modules/clockify/__init__.py rename to openpype/modules/clockify/__init__.py diff --git a/openpype/modules/default_modules/clockify/clockify_api.py b/openpype/modules/clockify/clockify_api.py similarity index 100% rename from openpype/modules/default_modules/clockify/clockify_api.py rename to openpype/modules/clockify/clockify_api.py diff --git a/openpype/modules/default_modules/clockify/clockify_module.py b/openpype/modules/clockify/clockify_module.py similarity index 100% rename from openpype/modules/default_modules/clockify/clockify_module.py rename to openpype/modules/clockify/clockify_module.py diff --git a/openpype/modules/default_modules/clockify/constants.py b/openpype/modules/clockify/constants.py similarity index 100% rename from openpype/modules/default_modules/clockify/constants.py rename to openpype/modules/clockify/constants.py diff --git a/openpype/modules/default_modules/clockify/ftrack/server/action_clockify_sync_server.py b/openpype/modules/clockify/ftrack/server/action_clockify_sync_server.py similarity index 100% rename from openpype/modules/default_modules/clockify/ftrack/server/action_clockify_sync_server.py rename to openpype/modules/clockify/ftrack/server/action_clockify_sync_server.py diff --git a/openpype/modules/default_modules/clockify/ftrack/user/action_clockify_sync_local.py b/openpype/modules/clockify/ftrack/user/action_clockify_sync_local.py similarity index 100% rename from openpype/modules/default_modules/clockify/ftrack/user/action_clockify_sync_local.py rename to openpype/modules/clockify/ftrack/user/action_clockify_sync_local.py diff --git a/openpype/modules/default_modules/clockify/launcher_actions/ClockifyStart.py b/openpype/modules/clockify/launcher_actions/ClockifyStart.py similarity index 100% rename from openpype/modules/default_modules/clockify/launcher_actions/ClockifyStart.py rename to openpype/modules/clockify/launcher_actions/ClockifyStart.py diff --git a/openpype/modules/default_modules/clockify/launcher_actions/ClockifySync.py b/openpype/modules/clockify/launcher_actions/ClockifySync.py similarity index 100% rename from openpype/modules/default_modules/clockify/launcher_actions/ClockifySync.py rename to openpype/modules/clockify/launcher_actions/ClockifySync.py diff --git a/openpype/modules/default_modules/clockify/widgets.py b/openpype/modules/clockify/widgets.py similarity index 100% rename from openpype/modules/default_modules/clockify/widgets.py rename to openpype/modules/clockify/widgets.py From 568726d30f25805190e26cdbb874180ac5117601 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 2 Dec 2021 16:29:53 +0100 Subject: [PATCH 048/492] moved log viewer module --- openpype/modules/base.py | 1 + openpype/modules/{default_modules => }/log_viewer/__init__.py | 0 .../modules/{default_modules => }/log_viewer/log_view_module.py | 0 .../modules/{default_modules => }/log_viewer/tray/__init__.py | 0 openpype/modules/{default_modules => }/log_viewer/tray/app.py | 0 openpype/modules/{default_modules => }/log_viewer/tray/models.py | 0 .../modules/{default_modules => }/log_viewer/tray/widgets.py | 0 7 files changed, 1 insertion(+) rename openpype/modules/{default_modules => }/log_viewer/__init__.py (100%) rename openpype/modules/{default_modules => }/log_viewer/log_view_module.py (100%) rename openpype/modules/{default_modules => }/log_viewer/tray/__init__.py (100%) rename openpype/modules/{default_modules => }/log_viewer/tray/app.py (100%) rename openpype/modules/{default_modules => }/log_viewer/tray/models.py (100%) rename openpype/modules/{default_modules => }/log_viewer/tray/widgets.py (100%) diff --git a/openpype/modules/base.py b/openpype/modules/base.py index fdca9e90be4..1dc9ccc718f 100644 --- a/openpype/modules/base.py +++ b/openpype/modules/base.py @@ -273,6 +273,7 @@ def _load_modules(): # Import default modules imported from 'openpype.modules' for default_module_name in ( + "log_viewer", "clockify", "settings_action", "launcher_action", diff --git a/openpype/modules/default_modules/log_viewer/__init__.py b/openpype/modules/log_viewer/__init__.py similarity index 100% rename from openpype/modules/default_modules/log_viewer/__init__.py rename to openpype/modules/log_viewer/__init__.py diff --git a/openpype/modules/default_modules/log_viewer/log_view_module.py b/openpype/modules/log_viewer/log_view_module.py similarity index 100% rename from openpype/modules/default_modules/log_viewer/log_view_module.py rename to openpype/modules/log_viewer/log_view_module.py diff --git a/openpype/modules/default_modules/log_viewer/tray/__init__.py b/openpype/modules/log_viewer/tray/__init__.py similarity index 100% rename from openpype/modules/default_modules/log_viewer/tray/__init__.py rename to openpype/modules/log_viewer/tray/__init__.py diff --git a/openpype/modules/default_modules/log_viewer/tray/app.py b/openpype/modules/log_viewer/tray/app.py similarity index 100% rename from openpype/modules/default_modules/log_viewer/tray/app.py rename to openpype/modules/log_viewer/tray/app.py diff --git a/openpype/modules/default_modules/log_viewer/tray/models.py b/openpype/modules/log_viewer/tray/models.py similarity index 100% rename from openpype/modules/default_modules/log_viewer/tray/models.py rename to openpype/modules/log_viewer/tray/models.py diff --git a/openpype/modules/default_modules/log_viewer/tray/widgets.py b/openpype/modules/log_viewer/tray/widgets.py similarity index 100% rename from openpype/modules/default_modules/log_viewer/tray/widgets.py rename to openpype/modules/log_viewer/tray/widgets.py From 38bfece22befe69a5320f70ba3d01a1804630666 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 2 Dec 2021 16:30:21 +0100 Subject: [PATCH 049/492] moved muster module --- openpype/modules/base.py | 1 + openpype/modules/{default_modules => }/muster/__init__.py | 0 openpype/modules/{default_modules => }/muster/muster.py | 0 openpype/modules/{default_modules => }/muster/rest_api.py | 0 openpype/modules/{default_modules => }/muster/widget_login.py | 0 5 files changed, 1 insertion(+) rename openpype/modules/{default_modules => }/muster/__init__.py (100%) rename openpype/modules/{default_modules => }/muster/muster.py (100%) rename openpype/modules/{default_modules => }/muster/rest_api.py (100%) rename openpype/modules/{default_modules => }/muster/widget_login.py (100%) diff --git a/openpype/modules/base.py b/openpype/modules/base.py index 1dc9ccc718f..2fa1967690b 100644 --- a/openpype/modules/base.py +++ b/openpype/modules/base.py @@ -274,6 +274,7 @@ def _load_modules(): # Import default modules imported from 'openpype.modules' for default_module_name in ( "log_viewer", + "muster", "clockify", "settings_action", "launcher_action", diff --git a/openpype/modules/default_modules/muster/__init__.py b/openpype/modules/muster/__init__.py similarity index 100% rename from openpype/modules/default_modules/muster/__init__.py rename to openpype/modules/muster/__init__.py diff --git a/openpype/modules/default_modules/muster/muster.py b/openpype/modules/muster/muster.py similarity index 100% rename from openpype/modules/default_modules/muster/muster.py rename to openpype/modules/muster/muster.py diff --git a/openpype/modules/default_modules/muster/rest_api.py b/openpype/modules/muster/rest_api.py similarity index 100% rename from openpype/modules/default_modules/muster/rest_api.py rename to openpype/modules/muster/rest_api.py diff --git a/openpype/modules/default_modules/muster/widget_login.py b/openpype/modules/muster/widget_login.py similarity index 100% rename from openpype/modules/default_modules/muster/widget_login.py rename to openpype/modules/muster/widget_login.py From db31a6aea9f28d04c196979f1eaf6f9dc230543c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 2 Dec 2021 16:30:46 +0100 Subject: [PATCH 050/492] moved python console interpreted module --- openpype/modules/base.py | 1 + .../{default_modules => }/python_console_interpreter/__init__.py | 0 .../{default_modules => }/python_console_interpreter/module.py | 0 .../python_console_interpreter/window/__init__.py | 0 .../python_console_interpreter/window/widgets.py | 0 5 files changed, 1 insertion(+) rename openpype/modules/{default_modules => }/python_console_interpreter/__init__.py (100%) rename openpype/modules/{default_modules => }/python_console_interpreter/module.py (100%) rename openpype/modules/{default_modules => }/python_console_interpreter/window/__init__.py (100%) rename openpype/modules/{default_modules => }/python_console_interpreter/window/widgets.py (100%) diff --git a/openpype/modules/base.py b/openpype/modules/base.py index 2fa1967690b..e03fa6c45bc 100644 --- a/openpype/modules/base.py +++ b/openpype/modules/base.py @@ -273,6 +273,7 @@ def _load_modules(): # Import default modules imported from 'openpype.modules' for default_module_name in ( + "python_console_interpreter", "log_viewer", "muster", "clockify", diff --git a/openpype/modules/default_modules/python_console_interpreter/__init__.py b/openpype/modules/python_console_interpreter/__init__.py similarity index 100% rename from openpype/modules/default_modules/python_console_interpreter/__init__.py rename to openpype/modules/python_console_interpreter/__init__.py diff --git a/openpype/modules/default_modules/python_console_interpreter/module.py b/openpype/modules/python_console_interpreter/module.py similarity index 100% rename from openpype/modules/default_modules/python_console_interpreter/module.py rename to openpype/modules/python_console_interpreter/module.py diff --git a/openpype/modules/default_modules/python_console_interpreter/window/__init__.py b/openpype/modules/python_console_interpreter/window/__init__.py similarity index 100% rename from openpype/modules/default_modules/python_console_interpreter/window/__init__.py rename to openpype/modules/python_console_interpreter/window/__init__.py diff --git a/openpype/modules/default_modules/python_console_interpreter/window/widgets.py b/openpype/modules/python_console_interpreter/window/widgets.py similarity index 100% rename from openpype/modules/default_modules/python_console_interpreter/window/widgets.py rename to openpype/modules/python_console_interpreter/window/widgets.py From 615f9145e140c42940e352bb4b1d9f4ecce35559 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 2 Dec 2021 16:31:22 +0100 Subject: [PATCH 051/492] moved slack module --- openpype/modules/base.py | 1 + .../modules/{default_modules => }/slack/README.md | 0 .../modules/{default_modules => }/slack/__init__.py | 0 .../slack/launch_hooks/pre_python2_vendor.py | 0 .../{default_modules => }/slack/manifest.yml | 0 .../slack/plugins/publish/collect_slack_family.py | 0 .../slack/plugins/publish/integrate_slack_api.py | 0 .../python2_vendor/python-slack-sdk-1/.appveyor.yml | 0 .../python2_vendor/python-slack-sdk-1/.coveragerc | 0 .../slack/python2_vendor/python-slack-sdk-1/.flake8 | 0 .../python-slack-sdk-1/.github/contributing.md | 0 .../python-slack-sdk-1/.github/issue_template.md | 0 .../python-slack-sdk-1/.github/maintainers_guide.md | 0 .../.github/pull_request_template.md | 0 .../python2_vendor/python-slack-sdk-1/.gitignore | 0 .../python2_vendor/python-slack-sdk-1/.travis.yml | 0 .../slack/python2_vendor/python-slack-sdk-1/LICENSE | 0 .../python2_vendor/python-slack-sdk-1/MANIFEST.in | 0 .../python2_vendor/python-slack-sdk-1/README.rst | 0 .../python-slack-sdk-1/docs-src/.gitignore | 0 .../python-slack-sdk-1/docs-src/Makefile | 0 .../docs-src/_themes/slack/conf.py | 0 .../docs-src/_themes/slack/layout.html | 0 .../docs-src/_themes/slack/localtoc.html | 0 .../docs-src/_themes/slack/relations.html | 0 .../docs-src/_themes/slack/sidebar.html | 0 .../docs-src/_themes/slack/static/default.css_t | 0 .../docs-src/_themes/slack/static/docs.css_t | 0 .../docs-src/_themes/slack/static/pygments.css_t | 0 .../docs-src/_themes/slack/theme.conf | 0 .../python-slack-sdk-1/docs-src/about.rst | 0 .../python-slack-sdk-1/docs-src/auth.rst | 0 .../python-slack-sdk-1/docs-src/basic_usage.rst | 0 .../python-slack-sdk-1/docs-src/changelog.rst | 0 .../python-slack-sdk-1/docs-src/conf.py | 0 .../python-slack-sdk-1/docs-src/conversations.rst | 0 .../python-slack-sdk-1/docs-src/faq.rst | 0 .../python-slack-sdk-1/docs-src/index.rst | 0 .../python-slack-sdk-1/docs-src/make.bat | 0 .../python-slack-sdk-1/docs-src/metadata.rst | 0 .../docs-src/real_time_messaging.rst | 0 .../slack/python2_vendor/python-slack-sdk-1/docs.sh | 0 .../python-slack-sdk-1/docs/.buildinfo | 0 .../python-slack-sdk-1/docs/.nojekyll | 0 .../python-slack-sdk-1/docs/_static/ajax-loader.gif | Bin .../python-slack-sdk-1/docs/_static/basic.css | 0 .../python-slack-sdk-1/docs/_static/classic.css | 0 .../docs/_static/comment-bright.png | Bin .../docs/_static/comment-close.png | Bin .../python-slack-sdk-1/docs/_static/comment.png | Bin .../python-slack-sdk-1/docs/_static/default.css | 0 .../python-slack-sdk-1/docs/_static/docs.css | 0 .../python-slack-sdk-1/docs/_static/doctools.js | 0 .../docs/_static/documentation_options.js | 0 .../docs/_static/down-pressed.png | Bin .../python-slack-sdk-1/docs/_static/down.png | Bin .../python-slack-sdk-1/docs/_static/file.png | Bin .../python-slack-sdk-1/docs/_static/jquery-3.2.1.js | 0 .../python-slack-sdk-1/docs/_static/jquery.js | 0 .../docs/_static/language_data.js | 0 .../python-slack-sdk-1/docs/_static/minus.png | Bin .../python-slack-sdk-1/docs/_static/plus.png | Bin .../python-slack-sdk-1/docs/_static/pygments.css | 0 .../python-slack-sdk-1/docs/_static/searchtools.js | 0 .../python-slack-sdk-1/docs/_static/sidebar.js | 0 .../docs/_static/underscore-1.3.1.js | 0 .../python-slack-sdk-1/docs/_static/underscore.js | 0 .../python-slack-sdk-1/docs/_static/up-pressed.png | Bin .../python-slack-sdk-1/docs/_static/up.png | Bin .../python-slack-sdk-1/docs/_static/websupport.js | 0 .../python-slack-sdk-1/docs/about.html | 0 .../python-slack-sdk-1/docs/auth.html | 0 .../python-slack-sdk-1/docs/basic_usage.html | 0 .../python-slack-sdk-1/docs/changelog.html | 0 .../python-slack-sdk-1/docs/conversations.html | 0 .../python2_vendor/python-slack-sdk-1/docs/faq.html | 0 .../python-slack-sdk-1/docs/genindex.html | 0 .../python-slack-sdk-1/docs/index.html | 0 .../python-slack-sdk-1/docs/metadata.html | 0 .../python-slack-sdk-1/docs/objects.inv | 0 .../docs/real_time_messaging.html | 0 .../python-slack-sdk-1/docs/search.html | 0 .../python-slack-sdk-1/docs/searchindex.js | 0 .../python-slack-sdk-1/requirements.txt | 0 .../python2_vendor/python-slack-sdk-1/setup.cfg | 0 .../python2_vendor/python-slack-sdk-1/setup.py | 0 .../python-slack-sdk-1/slackclient/__init__.py | 0 .../python-slack-sdk-1/slackclient/channel.py | 0 .../python-slack-sdk-1/slackclient/client.py | 0 .../python-slack-sdk-1/slackclient/exceptions.py | 0 .../python-slack-sdk-1/slackclient/im.py | 0 .../python-slack-sdk-1/slackclient/server.py | 0 .../python-slack-sdk-1/slackclient/slackrequest.py | 0 .../python-slack-sdk-1/slackclient/user.py | 0 .../python-slack-sdk-1/slackclient/util.py | 0 .../python-slack-sdk-1/slackclient/version.py | 0 .../python-slack-sdk-1/test_requirements.txt | 0 .../python-slack-sdk-1/tests/conftest.py | 0 .../tests/data/channel.created.json | 0 .../python-slack-sdk-1/tests/data/im.created.json | 0 .../python-slack-sdk-1/tests/data/rtm.start.json | 0 .../python-slack-sdk-1/tests/data/slack_logo.png | Bin .../python-slack-sdk-1/tests/test_channel.py | 0 .../python-slack-sdk-1/tests/test_server.py | 0 .../python-slack-sdk-1/tests/test_slackclient.py | 0 .../python-slack-sdk-1/tests/test_slackrequest.py | 0 .../slack/python2_vendor/python-slack-sdk-1/tox.ini | 0 .../{default_modules => }/slack/slack_module.py | 0 108 files changed, 1 insertion(+) rename openpype/modules/{default_modules => }/slack/README.md (100%) rename openpype/modules/{default_modules => }/slack/__init__.py (100%) rename openpype/modules/{default_modules => }/slack/launch_hooks/pre_python2_vendor.py (100%) rename openpype/modules/{default_modules => }/slack/manifest.yml (100%) rename openpype/modules/{default_modules => }/slack/plugins/publish/collect_slack_family.py (100%) rename openpype/modules/{default_modules => }/slack/plugins/publish/integrate_slack_api.py (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/.appveyor.yml (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/.coveragerc (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/.flake8 (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/.github/contributing.md (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/.github/issue_template.md (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/.github/maintainers_guide.md (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/.github/pull_request_template.md (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/.gitignore (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/.travis.yml (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/LICENSE (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/MANIFEST.in (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/README.rst (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs-src/.gitignore (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs-src/Makefile (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/conf.py (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/layout.html (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/localtoc.html (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/relations.html (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/sidebar.html (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/static/default.css_t (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/static/docs.css_t (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/static/pygments.css_t (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/theme.conf (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs-src/about.rst (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs-src/auth.rst (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs-src/basic_usage.rst (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs-src/changelog.rst (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs-src/conf.py (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs-src/conversations.rst (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs-src/faq.rst (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs-src/index.rst (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs-src/make.bat (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs-src/metadata.rst (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs-src/real_time_messaging.rst (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs.sh (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/.buildinfo (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/.nojekyll (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/_static/ajax-loader.gif (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/_static/basic.css (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/_static/classic.css (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/_static/comment-bright.png (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/_static/comment-close.png (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/_static/comment.png (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/_static/default.css (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/_static/docs.css (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/_static/doctools.js (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/_static/documentation_options.js (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/_static/down-pressed.png (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/_static/down.png (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/_static/file.png (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/_static/jquery-3.2.1.js (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/_static/jquery.js (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/_static/language_data.js (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/_static/minus.png (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/_static/plus.png (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/_static/pygments.css (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/_static/searchtools.js (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/_static/sidebar.js (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/_static/underscore-1.3.1.js (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/_static/underscore.js (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/_static/up-pressed.png (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/_static/up.png (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/_static/websupport.js (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/about.html (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/auth.html (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/basic_usage.html (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/changelog.html (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/conversations.html (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/faq.html (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/genindex.html (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/index.html (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/metadata.html (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/objects.inv (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/real_time_messaging.html (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/search.html (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/docs/searchindex.js (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/requirements.txt (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/setup.cfg (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/setup.py (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/slackclient/__init__.py (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/slackclient/channel.py (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/slackclient/client.py (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/slackclient/exceptions.py (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/slackclient/im.py (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/slackclient/server.py (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/slackclient/slackrequest.py (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/slackclient/user.py (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/slackclient/util.py (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/slackclient/version.py (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/test_requirements.txt (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/tests/conftest.py (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/tests/data/channel.created.json (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/tests/data/im.created.json (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/tests/data/rtm.start.json (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/tests/data/slack_logo.png (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/tests/test_channel.py (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/tests/test_server.py (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/tests/test_slackclient.py (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/tests/test_slackrequest.py (100%) rename openpype/modules/{default_modules => }/slack/python2_vendor/python-slack-sdk-1/tox.ini (100%) rename openpype/modules/{default_modules => }/slack/slack_module.py (100%) diff --git a/openpype/modules/base.py b/openpype/modules/base.py index e03fa6c45bc..eb40315dfa1 100644 --- a/openpype/modules/base.py +++ b/openpype/modules/base.py @@ -274,6 +274,7 @@ def _load_modules(): # Import default modules imported from 'openpype.modules' for default_module_name in ( "python_console_interpreter", + "slack", "log_viewer", "muster", "clockify", diff --git a/openpype/modules/default_modules/slack/README.md b/openpype/modules/slack/README.md similarity index 100% rename from openpype/modules/default_modules/slack/README.md rename to openpype/modules/slack/README.md diff --git a/openpype/modules/default_modules/slack/__init__.py b/openpype/modules/slack/__init__.py similarity index 100% rename from openpype/modules/default_modules/slack/__init__.py rename to openpype/modules/slack/__init__.py diff --git a/openpype/modules/default_modules/slack/launch_hooks/pre_python2_vendor.py b/openpype/modules/slack/launch_hooks/pre_python2_vendor.py similarity index 100% rename from openpype/modules/default_modules/slack/launch_hooks/pre_python2_vendor.py rename to openpype/modules/slack/launch_hooks/pre_python2_vendor.py diff --git a/openpype/modules/default_modules/slack/manifest.yml b/openpype/modules/slack/manifest.yml similarity index 100% rename from openpype/modules/default_modules/slack/manifest.yml rename to openpype/modules/slack/manifest.yml diff --git a/openpype/modules/default_modules/slack/plugins/publish/collect_slack_family.py b/openpype/modules/slack/plugins/publish/collect_slack_family.py similarity index 100% rename from openpype/modules/default_modules/slack/plugins/publish/collect_slack_family.py rename to openpype/modules/slack/plugins/publish/collect_slack_family.py diff --git a/openpype/modules/default_modules/slack/plugins/publish/integrate_slack_api.py b/openpype/modules/slack/plugins/publish/integrate_slack_api.py similarity index 100% rename from openpype/modules/default_modules/slack/plugins/publish/integrate_slack_api.py rename to openpype/modules/slack/plugins/publish/integrate_slack_api.py diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/.appveyor.yml b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/.appveyor.yml similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/.appveyor.yml rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/.appveyor.yml diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/.coveragerc b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/.coveragerc similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/.coveragerc rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/.coveragerc diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/.flake8 b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/.flake8 similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/.flake8 rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/.flake8 diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/.github/contributing.md b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/.github/contributing.md similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/.github/contributing.md rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/.github/contributing.md diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/.github/issue_template.md b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/.github/issue_template.md similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/.github/issue_template.md rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/.github/issue_template.md diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/.github/maintainers_guide.md b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/.github/maintainers_guide.md similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/.github/maintainers_guide.md rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/.github/maintainers_guide.md diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/.github/pull_request_template.md b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/.github/pull_request_template.md similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/.github/pull_request_template.md rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/.github/pull_request_template.md diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/.gitignore b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/.gitignore similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/.gitignore rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/.gitignore diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/.travis.yml b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/.travis.yml similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/.travis.yml rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/.travis.yml diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/LICENSE b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/LICENSE similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/LICENSE rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/LICENSE diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/MANIFEST.in b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/MANIFEST.in similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/MANIFEST.in rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/MANIFEST.in diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/README.rst b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/README.rst similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/README.rst rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/README.rst diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/.gitignore b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/.gitignore similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/.gitignore rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/.gitignore diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/Makefile b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/Makefile similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/Makefile rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/Makefile diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/conf.py b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/conf.py similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/conf.py rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/conf.py diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/layout.html b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/layout.html similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/layout.html rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/layout.html diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/localtoc.html b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/localtoc.html similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/localtoc.html rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/localtoc.html diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/relations.html b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/relations.html similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/relations.html rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/relations.html diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/sidebar.html b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/sidebar.html similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/sidebar.html rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/sidebar.html diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/static/default.css_t b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/static/default.css_t similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/static/default.css_t rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/static/default.css_t diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/static/docs.css_t b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/static/docs.css_t similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/static/docs.css_t rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/static/docs.css_t diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/static/pygments.css_t b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/static/pygments.css_t similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/static/pygments.css_t rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/static/pygments.css_t diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/theme.conf b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/theme.conf similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/theme.conf rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/_themes/slack/theme.conf diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/about.rst b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/about.rst similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/about.rst rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/about.rst diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/auth.rst b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/auth.rst similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/auth.rst rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/auth.rst diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/basic_usage.rst b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/basic_usage.rst similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/basic_usage.rst rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/basic_usage.rst diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/changelog.rst b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/changelog.rst similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/changelog.rst rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/changelog.rst diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/conf.py b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/conf.py similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/conf.py rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/conf.py diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/conversations.rst b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/conversations.rst similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/conversations.rst rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/conversations.rst diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/faq.rst b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/faq.rst similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/faq.rst rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/faq.rst diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/index.rst b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/index.rst similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/index.rst rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/index.rst diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/make.bat b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/make.bat similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/make.bat rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/make.bat diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/metadata.rst b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/metadata.rst similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/metadata.rst rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/metadata.rst diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/real_time_messaging.rst b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/real_time_messaging.rst similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs-src/real_time_messaging.rst rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs-src/real_time_messaging.rst diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs.sh b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs.sh similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs.sh rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs.sh diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/.buildinfo b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/.buildinfo similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/.buildinfo rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/.buildinfo diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/.nojekyll b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/.nojekyll similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/.nojekyll rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/.nojekyll diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/ajax-loader.gif b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/ajax-loader.gif similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/ajax-loader.gif rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/ajax-loader.gif diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/basic.css b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/basic.css similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/basic.css rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/basic.css diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/classic.css b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/classic.css similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/classic.css rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/classic.css diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/comment-bright.png b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/comment-bright.png similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/comment-bright.png rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/comment-bright.png diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/comment-close.png b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/comment-close.png similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/comment-close.png rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/comment-close.png diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/comment.png b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/comment.png similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/comment.png rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/comment.png diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/default.css b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/default.css similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/default.css rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/default.css diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/docs.css b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/docs.css similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/docs.css rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/docs.css diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/doctools.js b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/doctools.js similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/doctools.js rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/doctools.js diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/documentation_options.js b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/documentation_options.js similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/documentation_options.js rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/documentation_options.js diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/down-pressed.png b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/down-pressed.png similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/down-pressed.png rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/down-pressed.png diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/down.png b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/down.png similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/down.png rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/down.png diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/file.png b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/file.png similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/file.png rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/file.png diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/jquery-3.2.1.js b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/jquery-3.2.1.js similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/jquery-3.2.1.js rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/jquery-3.2.1.js diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/jquery.js b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/jquery.js similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/jquery.js rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/jquery.js diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/language_data.js b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/language_data.js similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/language_data.js rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/language_data.js diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/minus.png b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/minus.png similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/minus.png rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/minus.png diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/plus.png b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/plus.png similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/plus.png rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/plus.png diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/pygments.css b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/pygments.css similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/pygments.css rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/pygments.css diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/searchtools.js b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/searchtools.js similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/searchtools.js rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/searchtools.js diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/sidebar.js b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/sidebar.js similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/sidebar.js rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/sidebar.js diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/underscore-1.3.1.js b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/underscore-1.3.1.js similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/underscore-1.3.1.js rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/underscore-1.3.1.js diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/underscore.js b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/underscore.js similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/underscore.js rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/underscore.js diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/up-pressed.png b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/up-pressed.png similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/up-pressed.png rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/up-pressed.png diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/up.png b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/up.png similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/up.png rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/up.png diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/websupport.js b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/websupport.js similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/websupport.js rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/_static/websupport.js diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/about.html b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/about.html similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/about.html rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/about.html diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/auth.html b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/auth.html similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/auth.html rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/auth.html diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/basic_usage.html b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/basic_usage.html similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/basic_usage.html rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/basic_usage.html diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/changelog.html b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/changelog.html similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/changelog.html rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/changelog.html diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/conversations.html b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/conversations.html similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/conversations.html rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/conversations.html diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/faq.html b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/faq.html similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/faq.html rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/faq.html diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/genindex.html b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/genindex.html similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/genindex.html rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/genindex.html diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/index.html b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/index.html similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/index.html rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/index.html diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/metadata.html b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/metadata.html similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/metadata.html rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/metadata.html diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/objects.inv b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/objects.inv similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/objects.inv rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/objects.inv diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/real_time_messaging.html b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/real_time_messaging.html similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/real_time_messaging.html rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/real_time_messaging.html diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/search.html b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/search.html similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/search.html rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/search.html diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/searchindex.js b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/searchindex.js similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/docs/searchindex.js rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/docs/searchindex.js diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/requirements.txt b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/requirements.txt similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/requirements.txt rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/requirements.txt diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/setup.cfg b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/setup.cfg similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/setup.cfg rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/setup.cfg diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/setup.py b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/setup.py similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/setup.py rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/setup.py diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/slackclient/__init__.py b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/slackclient/__init__.py similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/slackclient/__init__.py rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/slackclient/__init__.py diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/slackclient/channel.py b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/slackclient/channel.py similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/slackclient/channel.py rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/slackclient/channel.py diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/slackclient/client.py b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/slackclient/client.py similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/slackclient/client.py rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/slackclient/client.py diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/slackclient/exceptions.py b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/slackclient/exceptions.py similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/slackclient/exceptions.py rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/slackclient/exceptions.py diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/slackclient/im.py b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/slackclient/im.py similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/slackclient/im.py rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/slackclient/im.py diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/slackclient/server.py b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/slackclient/server.py similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/slackclient/server.py rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/slackclient/server.py diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/slackclient/slackrequest.py b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/slackclient/slackrequest.py similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/slackclient/slackrequest.py rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/slackclient/slackrequest.py diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/slackclient/user.py b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/slackclient/user.py similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/slackclient/user.py rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/slackclient/user.py diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/slackclient/util.py b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/slackclient/util.py similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/slackclient/util.py rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/slackclient/util.py diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/slackclient/version.py b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/slackclient/version.py similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/slackclient/version.py rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/slackclient/version.py diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/test_requirements.txt b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/test_requirements.txt similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/test_requirements.txt rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/test_requirements.txt diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/tests/conftest.py b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/tests/conftest.py similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/tests/conftest.py rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/tests/conftest.py diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/tests/data/channel.created.json b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/tests/data/channel.created.json similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/tests/data/channel.created.json rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/tests/data/channel.created.json diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/tests/data/im.created.json b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/tests/data/im.created.json similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/tests/data/im.created.json rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/tests/data/im.created.json diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/tests/data/rtm.start.json b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/tests/data/rtm.start.json similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/tests/data/rtm.start.json rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/tests/data/rtm.start.json diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/tests/data/slack_logo.png b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/tests/data/slack_logo.png similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/tests/data/slack_logo.png rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/tests/data/slack_logo.png diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/tests/test_channel.py b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/tests/test_channel.py similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/tests/test_channel.py rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/tests/test_channel.py diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/tests/test_server.py b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/tests/test_server.py similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/tests/test_server.py rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/tests/test_server.py diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/tests/test_slackclient.py b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/tests/test_slackclient.py similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/tests/test_slackclient.py rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/tests/test_slackclient.py diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/tests/test_slackrequest.py b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/tests/test_slackrequest.py similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/tests/test_slackrequest.py rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/tests/test_slackrequest.py diff --git a/openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/tox.ini b/openpype/modules/slack/python2_vendor/python-slack-sdk-1/tox.ini similarity index 100% rename from openpype/modules/default_modules/slack/python2_vendor/python-slack-sdk-1/tox.ini rename to openpype/modules/slack/python2_vendor/python-slack-sdk-1/tox.ini diff --git a/openpype/modules/default_modules/slack/slack_module.py b/openpype/modules/slack/slack_module.py similarity index 100% rename from openpype/modules/default_modules/slack/slack_module.py rename to openpype/modules/slack/slack_module.py From 8b79961e286a18bb8dcb893c64470bee99177e3d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 2 Dec 2021 16:31:34 +0100 Subject: [PATCH 052/492] moved webserver module --- openpype/modules/base.py | 1 + openpype/modules/{default_modules => }/webserver/__init__.py | 0 openpype/modules/{default_modules => }/webserver/base_routes.py | 0 .../{default_modules => }/webserver/host_console_listener.py | 0 openpype/modules/{default_modules => }/webserver/server.py | 0 .../modules/{default_modules => }/webserver/webserver_module.py | 0 6 files changed, 1 insertion(+) rename openpype/modules/{default_modules => }/webserver/__init__.py (100%) rename openpype/modules/{default_modules => }/webserver/base_routes.py (100%) rename openpype/modules/{default_modules => }/webserver/host_console_listener.py (100%) rename openpype/modules/{default_modules => }/webserver/server.py (100%) rename openpype/modules/{default_modules => }/webserver/webserver_module.py (100%) diff --git a/openpype/modules/base.py b/openpype/modules/base.py index eb40315dfa1..e300dd08063 100644 --- a/openpype/modules/base.py +++ b/openpype/modules/base.py @@ -273,6 +273,7 @@ def _load_modules(): # Import default modules imported from 'openpype.modules' for default_module_name in ( + "webserver", "python_console_interpreter", "slack", "log_viewer", diff --git a/openpype/modules/default_modules/webserver/__init__.py b/openpype/modules/webserver/__init__.py similarity index 100% rename from openpype/modules/default_modules/webserver/__init__.py rename to openpype/modules/webserver/__init__.py diff --git a/openpype/modules/default_modules/webserver/base_routes.py b/openpype/modules/webserver/base_routes.py similarity index 100% rename from openpype/modules/default_modules/webserver/base_routes.py rename to openpype/modules/webserver/base_routes.py diff --git a/openpype/modules/default_modules/webserver/host_console_listener.py b/openpype/modules/webserver/host_console_listener.py similarity index 100% rename from openpype/modules/default_modules/webserver/host_console_listener.py rename to openpype/modules/webserver/host_console_listener.py diff --git a/openpype/modules/default_modules/webserver/server.py b/openpype/modules/webserver/server.py similarity index 100% rename from openpype/modules/default_modules/webserver/server.py rename to openpype/modules/webserver/server.py diff --git a/openpype/modules/default_modules/webserver/webserver_module.py b/openpype/modules/webserver/webserver_module.py similarity index 100% rename from openpype/modules/default_modules/webserver/webserver_module.py rename to openpype/modules/webserver/webserver_module.py From d65431c34b120c29b3a791cf7839092ae34db096 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 2 Dec 2021 17:20:29 +0100 Subject: [PATCH 053/492] OP-2042 - remove pytest deprecation warnings --- openpype/pype_commands.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openpype/pype_commands.py b/openpype/pype_commands.py index 519e7c285b7..7f6e5f1c0cf 100644 --- a/openpype/pype_commands.py +++ b/openpype/pype_commands.py @@ -216,6 +216,7 @@ def remotepublishfromapp(project, batch_dir, host_name, task_name, app_name ) + print("env:: {}".format(env)) os.environ.update(env) os.environ["OPENPYPE_PUBLISH_DATA"] = batch_dir @@ -364,7 +365,10 @@ def run_tests(self, folder, mark, pyargs): if pyargs: pyargs_str = "--pyargs {}".format(pyargs) - cmd = "pytest {} {} {}".format(folder, mark_str, pyargs_str) + depr_str = "--disable-pytest-warnings" + + cmd = "pytest {} {} {} {}".format(depr_str, folder, + mark_str, pyargs_str) print("Running {}".format(cmd)) subprocess.run(cmd) From 9dbae0e52e43b569051c28e4ef74179548f68065 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 2 Dec 2021 17:50:18 +0100 Subject: [PATCH 054/492] OP-2042 - added NUKE_PATH to settings It was overwriting existing value of NUKE_PATH before --- openpype/settings/defaults/system_settings/applications.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/settings/defaults/system_settings/applications.json b/openpype/settings/defaults/system_settings/applications.json index 8c119658bed..4e502010366 100644 --- a/openpype/settings/defaults/system_settings/applications.json +++ b/openpype/settings/defaults/system_settings/applications.json @@ -139,7 +139,7 @@ "icon": "{}/app_icons/nuke.png", "host_name": "nuke", "environment": { - "NUKE_PATH": "{OPENPYPE_STUDIO_PLUGINS}/nuke" + "NUKE_PATH": ["{NUKE_PATH}", "{OPENPYPE_STUDIO_PLUGINS}/nuke"] }, "variants": { "13-0": { @@ -245,7 +245,7 @@ "icon": "{}/app_icons/nuke.png", "host_name": "nuke", "environment": { - "NUKE_PATH": "{OPENPYPE_STUDIO_PLUGINS}/nuke" + "NUKE_PATH": ["{NUKE_PATH}", "{OPENPYPE_STUDIO_PLUGINS}/nuke"] }, "variants": { "13-0": { From 591e81a4148396dd5e6e71a0f55133f7c6b13a1d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 2 Dec 2021 17:51:45 +0100 Subject: [PATCH 055/492] OP-2042 - comment out examples --- tests/lib/db_handler.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/tests/lib/db_handler.py b/tests/lib/db_handler.py index 88cde4d05f0..0aa4c69ca6d 100644 --- a/tests/lib/db_handler.py +++ b/tests/lib/db_handler.py @@ -220,15 +220,16 @@ def _import_query(self, uri, sql_url, return query -handler = DBHandler(uri="mongodb://localhost:27017") -# -backup_dir = "c:\\projects\\test_nuke_publish\\input\\dumps" +# Examples +# handler = DBHandler(uri="mongodb://localhost:27017") # # -handler.backup_to_dump("avalon", backup_dir, True, collection="test_project") -#handler.setup_from_dump("test_db", backup_dir, True, db_name_out="avalon", collection="test_project") -# # handler.setup_from_sql_file("test_db", "c:\\projects\\sql\\item.sql", -# # collection="test_project", -# # drop=False, mode="upsert") -# handler.setup_from_sql("test_db", "c:\\projects\\sql", +# backup_dir = "c:\\projects\\test_nuke_publish\\input\\dumps" +# # # +# handler.backup_to_dump("avalon", backup_dir, True, collection="test_project") +# handler.setup_from_dump("test_db", backup_dir, True, db_name_out="avalon", collection="test_project") +# handler.setup_from_sql_file("test_db", "c:\\projects\\sql\\item.sql", # collection="test_project", # drop=False, mode="upsert") +# handler.setup_from_sql("test_db", "c:\\projects\\sql", +# collection="test_project", +# drop=False, mode="upsert") From fe86bbde299ffc9dd19b3bc9225cfb289c21ee3c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 2 Dec 2021 18:12:54 +0100 Subject: [PATCH 056/492] OP-2042 - working example of test publish in Nuke --- .../hosts/nuke/test_publish_in_nuke.py | 42 ++++++++----------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/tests/integration/hosts/nuke/test_publish_in_nuke.py b/tests/integration/hosts/nuke/test_publish_in_nuke.py index abadb0fb924..574ad8de00b 100644 --- a/tests/integration/hosts/nuke/test_publish_in_nuke.py +++ b/tests/integration/hosts/nuke/test_publish_in_nuke.py @@ -14,17 +14,20 @@ class TestPublishInNuke(PublishTest): Uses generic TestCase to prepare fixtures for test data, testing DBs, env vars. - Opens Maya, run publish on prepared workile. + Opens Nuke, run publish on prepared workile. Then checks content of DB (if subset, version, representations were created. Checks tmp folder if all expected files were published. + How to run: + {OPENPYPE_ROOT}/.venv/Scripts/python.exe {OPENPYPE_ROOT}/start.py runtests ../tests/integration/hosts/nuke # noqa: E501 + """ - PERSIST = True + PERSIST = True # True - keep test_db, test_openpype, outputted test files TEST_FILES = [ - ("1635L4gww9nEkP-1EclfWXNdeDuRjDhey", "test_Nuke_publish.zip", "") + ("1SUurHj2aiQ21ZIMJfGVBI2KjR8kIjBGI", "test_Nuke_publish.zip", "") ] APP = "nuke" @@ -40,7 +43,6 @@ def last_workfile_path(self, download_test_data): Maya expects workfile in proper folder, so copy is done first. """ - print("last_workfile_path") log.info("log last_workfile_path") src_path = os.path.join( download_test_data, @@ -62,20 +64,14 @@ def last_workfile_path(self, download_test_data): @pytest.fixture(scope="module") def startup_scripts(self, monkeypatch_session, download_test_data): """Points Nuke to userSetup file from input data""" - print("startup_scripts") - log.info("log startup_scripts") startup_path = os.path.join(download_test_data, "input", "startup") - startup_path = "C:\\projects\\test_nuke_publish\\input\\startup" - original_pythonpath = os.environ.get("NUKE_PATH") + original_nuke_path = os.environ.get("NUKE_PATH", "") monkeypatch_session.setenv("NUKE_PATH", - "{}{}{}".format(original_pythonpath, - os.pathsep, - startup_path)) - print("NUKE_PATH:: {}{}{}".format(startup_path, + "{}{}{}".format(startup_path, os.pathsep, - original_pythonpath)) + original_nuke_path)) def test_db_asserts(self, dbcon, publish_finished): """Host and input data dependent expected results in DB.""" @@ -88,25 +84,21 @@ def test_db_asserts(self, dbcon, publish_finished): "Only versions with 1 expected" assert 1 == dbcon.count_documents({"type": "subset", - "name": "modelMain"}), \ - "modelMain subset must be present" + "name": "renderCompositingInNukeMain"} # noqa: E501 + ), \ + "renderCompositingInNukeMain subset must be present" assert 1 == dbcon.count_documents({"type": "subset", "name": "workfileTest_task"}), \ "workfileTest_task subset must be present" - assert 11 == dbcon.count_documents({"type": "representation"}), \ + assert 10 == dbcon.count_documents({"type": "representation"}), \ "Not expected no of representations" - assert 2 == dbcon.count_documents({"type": "representation", - "context.subset": "modelMain", - "context.ext": "abc"}), \ - "Not expected no of representations with ext 'abc'" - - assert 2 == dbcon.count_documents({"type": "representation", - "context.subset": "modelMain", - "context.ext": "ma"}), \ - "Not expected no of representations with ext 'abc'" + assert 1 == dbcon.count_documents({"type": "representation", + "context.subset": "renderCompositingInNukeMain", # noqa: E501 + "context.ext": "exr"}), \ + "Not expected no of representations with ext 'exr'" if __name__ == "__main__": From e35920f9bed75a55770b219d0374d0e19b07ee7b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 2 Dec 2021 18:40:39 +0100 Subject: [PATCH 057/492] OP-2042 - replaced testing zip files Zip files now stored on OP shared GDrive PS implementation is not working, fixed in OP-2019 --- tests/integration/hosts/maya/test_publish_in_maya.py | 9 +++++++-- .../hosts/photoshop/test_publish_in_photoshop.py | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/tests/integration/hosts/maya/test_publish_in_maya.py b/tests/integration/hosts/maya/test_publish_in_maya.py index 1babf30029d..566829b2e2d 100644 --- a/tests/integration/hosts/maya/test_publish_in_maya.py +++ b/tests/integration/hosts/maya/test_publish_in_maya.py @@ -13,17 +13,22 @@ class TestPublishInMaya(PublishTest): Uses generic TestCase to prepare fixtures for test data, testing DBs, env vars. - Opens Maya, run publish on prepared workile. + Always pulls and uses test data from GDrive! + + Opens Maya, runs publish on prepared workile. Then checks content of DB (if subset, version, representations were created. Checks tmp folder if all expected files were published. + How to run: + {OPENPYPE_ROOT}/.venv/Scripts/python.exe {OPENPYPE_ROOT}/start.py runtests ../tests/integration/hosts/maya # noqa: E501 + """ PERSIST = True TEST_FILES = [ - ("1pOwjA_VVBc6ooTZyFxtAwLS2KZHaBlkY", "test_maya_publish.zip", "") + ("1BTSIIULJTuDc8VvXseuiJV_fL6-Bu7FP", "test_maya_publish.zip", "") ] APP = "maya" diff --git a/tests/integration/hosts/photoshop/test_publish_in_photoshop.py b/tests/integration/hosts/photoshop/test_publish_in_photoshop.py index b634d422f34..3ef40f30417 100644 --- a/tests/integration/hosts/photoshop/test_publish_in_photoshop.py +++ b/tests/integration/hosts/photoshop/test_publish_in_photoshop.py @@ -11,17 +11,22 @@ class TestPublishInPhotoshop(PublishTest): Uses generic TestCase to prepare fixtures for test data, testing DBs, env vars. - Opens Maya, run publish on prepared workile. + Always pulls and uses test data from GDrive! + + Opens Photoshop, runs publish on prepared workile. Then checks content of DB (if subset, version, representations were created. Checks tmp folder if all expected files were published. + How to run: + {OPENPYPE_ROOT}/.venv/Scripts/python.exe {OPENPYPE_ROOT}/start.py runtests ../tests/integration/hosts/photoshop # noqa: E501 + """ PERSIST = True TEST_FILES = [ - ("1Bciy2pCwMKl1UIpxuPnlX_LHMo_Xkq0K", "test_photoshop_publish.zip", "") + ("1zD2v5cBgkyOm_xIgKz3WKn8aFB_j8qC-", "test_photoshop_publish.zip", "") ] APP = "photoshop" From e25fcade98ac6c787f99e50214b6b52099abd80f Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Thu, 2 Dec 2021 19:02:18 +0100 Subject: [PATCH 058/492] move version detection --- igniter/bootstrap_repos.py | 210 +++++++++++++++++++++++++++++++++++++ 1 file changed, 210 insertions(+) diff --git a/igniter/bootstrap_repos.py b/igniter/bootstrap_repos.py index 151597e505c..b0f3b482acc 100644 --- a/igniter/bootstrap_repos.py +++ b/igniter/bootstrap_repos.py @@ -232,6 +232,216 @@ def __hash__(self): else: return hash(str(self)) + @staticmethod + def is_version_in_dir( + dir_item: Path, version: OpenPypeVersion) -> Tuple[bool, str]: + """Test if path item is OpenPype version matching detected version. + + If item is directory that might (based on it's name) + contain OpenPype version, check if it really does contain + OpenPype and that their versions matches. + + Args: + dir_item (Path): Directory to test. + version (OpenPypeVersion): OpenPype version detected + from name. + + Returns: + Tuple: State and reason, True if it is valid OpenPype version, + False otherwise. + + """ + try: + # add one 'openpype' level as inside dir there should + # be many other repositories. + version_str = OpenPypeVersion.get_version_string_from_directory( + dir_item) # noqa: E501 + version_check = OpenPypeVersion(version=version_str) + except ValueError: + return False, f"cannot determine version from {dir_item}" + + version_main = version_check.get_main_version() + detected_main = version.get_main_version() + if version_main != detected_main: + return False, (f"dir version ({version}) and " + f"its content version ({version_check}) " + "doesn't match. Skipping.") + return True, "Versions match" + + @staticmethod + def is_version_in_zip( + zip_item: Path, version: OpenPypeVersion) -> Tuple[bool, str]: + """Test if zip path is OpenPype version matching detected version. + + Open zip file, look inside and parse version from OpenPype + inside it. If there is none, or it is different from + version specified in file name, skip it. + + Args: + zip_item (Path): Zip file to test. + version (OpenPypeVersion): Pype version detected + from name. + + Returns: + Tuple: State and reason, True if it is valid OpenPype version, + False otherwise. + + """ + # skip non-zip files + if zip_item.suffix.lower() != ".zip": + return False, "Not a zip" + + try: + with ZipFile(zip_item, "r") as zip_file: + with zip_file.open( + "openpype/version.py") as version_file: + zip_version = {} + exec(version_file.read(), zip_version) + try: + version_check = OpenPypeVersion( + version=zip_version["__version__"]) + except ValueError as e: + return False, str(e) + + version_main = version_check.get_main_version() # + # noqa: E501 + detected_main = version.get_main_version() + # noqa: E501 + + if version_main != detected_main: + return False, (f"zip version ({version}) " + f"and its content version " + f"({version_check}) " + "doesn't match. Skipping.") + except BadZipFile: + return False, f"{zip_item} is not a zip file" + except KeyError: + return False, "Zip does not contain OpenPype" + return True, "Versions match" + + @staticmethod + def get_version_string_from_directory(repo_dir: Path) -> Union[str, None]: + """Get version of OpenPype in given directory. + + Note: in frozen OpenPype installed in user data dir, this must point + one level deeper as it is: + `openpype-version-v3.0.0/openpype/version.py` + + Args: + repo_dir (Path): Path to OpenPype repo. + + Returns: + str: version string. + None: if OpenPype is not found. + + """ + # try to find version + version_file = Path(repo_dir) / "openpype" / "version.py" + if not version_file.exists(): + return None + + version = {} + with version_file.open("r") as fp: + exec(fp.read(), version) + + return version['__version__'] + + @staticmethod + def get_available_versions( + staging: bool = False, local: bool = False) -> List: + """Get ordered dict of detected OpenPype version. + + Resolution order for OpenPype is following: + + 1) First we test for ``OPENPYPE_PATH`` environment variable + 2) We try to find ``openPypePath`` in registry setting + 3) We use user data directory + + Only versions from 3) will be listed when ``local`` is set to True. + + Args: + staging (bool, optional): List staging versions if True. + local (bool, optional): List only local versions. + + """ + registry = OpenPypeSettingsRegistry() + dir_to_search = Path(user_data_dir("openpype", "pypeclub")) + user_versions = OpenPypeVersion.get_versions_from_directory( + dir_to_search) + # if we have openpype_path specified, search only there. + + if not local: + if os.getenv("OPENPYPE_PATH"): + if Path(os.getenv("OPENPYPE_PATH")).exists(): + dir_to_search = Path(os.getenv("OPENPYPE_PATH")) + else: + try: + registry_dir = Path( + str(registry.get_item("openPypePath"))) + if registry_dir.exists(): + dir_to_search = registry_dir + + except ValueError: + # nothing found in registry, we'll use data dir + pass + + openpype_versions = OpenPypeVersion.get_versions_from_directory( + dir_to_search) + openpype_versions += user_versions + + # remove duplicates and staging/production + openpype_versions = [ + v for v in openpype_versions if v.is_staging() == staging + ] + openpype_versions = sorted(list(set(openpype_versions))) + + return openpype_versions + + @staticmethod + def get_versions_from_directory(openpype_dir: Path) -> List: + """Get all detected OpenPype versions in directory. + + Args: + openpype_dir (Path): Directory to scan. + + Returns: + list of OpenPypeVersion + + Throws: + ValueError: if invalid path is specified. + + """ + if not openpype_dir.exists() and not openpype_dir.is_dir(): + raise ValueError("specified directory is invalid") + + _openpype_versions = [] + # iterate over directory in first level and find all that might + # contain OpenPype. + for item in openpype_dir.iterdir(): + + # if file, strip extension, in case of dir not. + name = item.name if item.is_dir() else item.stem + result = OpenPypeVersion.version_in_str(name) + + if result: + detected_version: OpenPypeVersion + detected_version = result + + if item.is_dir() and not OpenPypeVersion.is_version_in_dir( + item, detected_version + )[0]: + continue + + if item.is_file() and not OpenPypeVersion.is_version_in_zip( + item, detected_version + )[0]: + continue + + detected_version.path = item + _openpype_versions.append(detected_version) + + return sorted(_openpype_versions) + class BootstrapRepos: """Class for bootstrapping local OpenPype installation. From d4e5ab90cf3bc81a8ffe94b5b8736d7403adb2c3 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 2 Dec 2021 19:02:35 +0100 Subject: [PATCH 059/492] OP-2042 - added better documentation how to run it --- tests/integration/hosts/maya/test_publish_in_maya.py | 1 + tests/integration/hosts/nuke/test_publish_in_nuke.py | 1 + tests/integration/hosts/photoshop/test_publish_in_photoshop.py | 1 + 3 files changed, 3 insertions(+) diff --git a/tests/integration/hosts/maya/test_publish_in_maya.py b/tests/integration/hosts/maya/test_publish_in_maya.py index 566829b2e2d..b53b26f66dd 100644 --- a/tests/integration/hosts/maya/test_publish_in_maya.py +++ b/tests/integration/hosts/maya/test_publish_in_maya.py @@ -22,6 +22,7 @@ class TestPublishInMaya(PublishTest): Checks tmp folder if all expected files were published. How to run: + (in cmd with activated {OPENPYPE_ROOT}/.venv) {OPENPYPE_ROOT}/.venv/Scripts/python.exe {OPENPYPE_ROOT}/start.py runtests ../tests/integration/hosts/maya # noqa: E501 """ diff --git a/tests/integration/hosts/nuke/test_publish_in_nuke.py b/tests/integration/hosts/nuke/test_publish_in_nuke.py index 574ad8de00b..6dc880d757a 100644 --- a/tests/integration/hosts/nuke/test_publish_in_nuke.py +++ b/tests/integration/hosts/nuke/test_publish_in_nuke.py @@ -21,6 +21,7 @@ class TestPublishInNuke(PublishTest): Checks tmp folder if all expected files were published. How to run: + (in cmd with activated {OPENPYPE_ROOT}/.venv) {OPENPYPE_ROOT}/.venv/Scripts/python.exe {OPENPYPE_ROOT}/start.py runtests ../tests/integration/hosts/nuke # noqa: E501 """ diff --git a/tests/integration/hosts/photoshop/test_publish_in_photoshop.py b/tests/integration/hosts/photoshop/test_publish_in_photoshop.py index 3ef40f30417..43e03b2cc3b 100644 --- a/tests/integration/hosts/photoshop/test_publish_in_photoshop.py +++ b/tests/integration/hosts/photoshop/test_publish_in_photoshop.py @@ -20,6 +20,7 @@ class TestPublishInPhotoshop(PublishTest): Checks tmp folder if all expected files were published. How to run: + (in cmd with activated {OPENPYPE_ROOT}/.venv) {OPENPYPE_ROOT}/.venv/Scripts/python.exe {OPENPYPE_ROOT}/start.py runtests ../tests/integration/hosts/photoshop # noqa: E501 """ From 5d8550399535492c6bc52ba58f5c2aff9200f066 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 2 Dec 2021 19:03:43 +0100 Subject: [PATCH 060/492] OP-2042 - removed forgotten app for debugging --- openpype/hooks/pre_foundry_apps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hooks/pre_foundry_apps.py b/openpype/hooks/pre_foundry_apps.py index 70554cbedbf..85f68c6b604 100644 --- a/openpype/hooks/pre_foundry_apps.py +++ b/openpype/hooks/pre_foundry_apps.py @@ -13,7 +13,7 @@ class LaunchFoundryAppsWindows(PreLaunchHook): # Should be as last hook because must change launch arguments to string order = 1000 - app_groups = ["nuke", "nukex", "hiero", "nukestudio", "aftereffects"] + app_groups = ["nuke", "nukex", "hiero", "nukestudio"] platforms = ["windows"] def execute(self): From 1762f4635b5fe520825343b4b80ff7fec02b9c42 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 2 Dec 2021 19:04:44 +0100 Subject: [PATCH 061/492] OP-2042 - fixed wrong value Belongs to merged PR for AE testing --- openpype/lib/remote_publish.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/lib/remote_publish.py b/openpype/lib/remote_publish.py index 4b4d233f1ec..6c594b50e86 100644 --- a/openpype/lib/remote_publish.py +++ b/openpype/lib/remote_publish.py @@ -27,7 +27,7 @@ def headless_publish(log, close_plugin_name=None, is_test=False): publish_and_log(dbcon, _id, log, close_plugin_name) else: - publish(log, 'CloseAE') + publish(log, close_plugin_name) def get_webpublish_conn(): From 07fdcc6f4de08087a4f22343bb61a83cc65b0df8 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 2 Dec 2021 19:07:46 +0100 Subject: [PATCH 062/492] OP-2019 - fixed wrong value --- openpype/lib/remote_publish.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/lib/remote_publish.py b/openpype/lib/remote_publish.py index 3483898af73..b91c5f01fc8 100644 --- a/openpype/lib/remote_publish.py +++ b/openpype/lib/remote_publish.py @@ -27,7 +27,7 @@ def headless_publish(log, close_plugin_name=None, is_test=False): publish_and_log(dbcon, _id, log, close_plugin_name) else: - publish(log, 'CloseAE') + publish(log, close_plugin_name) def get_webpublish_conn(): From 9a5f90620ed57bffe00888d5319d928064636b64 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 2 Dec 2021 19:27:50 +0100 Subject: [PATCH 063/492] Commit current develop avalon-core --- repos/avalon-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repos/avalon-core b/repos/avalon-core index e37f4f92ed2..1d94a75ddf3 160000 --- a/repos/avalon-core +++ b/repos/avalon-core @@ -1 +1 @@ -Subproject commit e37f4f92ed25f89c870fdcb7f9538da7d0d7de90 +Subproject commit 1d94a75ddf37a354955a9e7f4bb8695187f3e0ed From ceeaf28dd7e461cd2271796092651b014f6c6f8f Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 Dec 2021 10:14:14 +0100 Subject: [PATCH 064/492] OP-2042 - clean up files from different PR --- .../aftereffects/plugins/publish/closeAE.py | 29 ------ .../plugins/publish/extract_local_render.py | 3 +- openpype/lib/remote_publish.py | 46 --------- .../system_settings/applications.json | 17 ---- .../test_publish_in_aftereffects.py | 96 ------------------- 5 files changed, 2 insertions(+), 189 deletions(-) delete mode 100644 openpype/hosts/aftereffects/plugins/publish/closeAE.py delete mode 100644 tests/integration/hosts/aftereffects/test_publish_in_aftereffects.py diff --git a/openpype/hosts/aftereffects/plugins/publish/closeAE.py b/openpype/hosts/aftereffects/plugins/publish/closeAE.py deleted file mode 100644 index e6e96234743..00000000000 --- a/openpype/hosts/aftereffects/plugins/publish/closeAE.py +++ /dev/null @@ -1,29 +0,0 @@ -# -*- coding: utf-8 -*- -"""Close AE after publish. For Webpublishing only.""" -import os - -import pyblish.api - -from avalon import aftereffects - - -class CloseAE(pyblish.api.ContextPlugin): - """Close AE after publish. For Webpublishing only. - """ - - order = pyblish.api.IntegratorOrder + 14 - label = "Close AE" - optional = True - active = True - - hosts = ["aftereffects"] - targets = ["remotepublish"] - - def process(self, context): - self.log.info("CloseAE") - - stub = aftereffects.stub() - self.log.info("Shutting down AE") - stub.save() - stub.close() - self.log.info("AE closed") diff --git a/openpype/hosts/aftereffects/plugins/publish/extract_local_render.py b/openpype/hosts/aftereffects/plugins/publish/extract_local_render.py index b36ab24bde9..37337e7fee5 100644 --- a/openpype/hosts/aftereffects/plugins/publish/extract_local_render.py +++ b/openpype/hosts/aftereffects/plugins/publish/extract_local_render.py @@ -19,9 +19,10 @@ def process(self, instance): staging_dir = instance.data["stagingDir"] self.log.info("staging_dir::{}".format(staging_dir)) + stub.render(staging_dir) + # pull file name from Render Queue Output module render_q = stub.get_render_info() - stub.render(staging_dir) if not render_q: raise ValueError("No file extension set in Render Queue") _, ext = os.path.splitext(os.path.basename(render_q.file_name)) diff --git a/openpype/lib/remote_publish.py b/openpype/lib/remote_publish.py index 6c594b50e86..d7db4d1ab9c 100644 --- a/openpype/lib/remote_publish.py +++ b/openpype/lib/remote_publish.py @@ -11,25 +11,6 @@ from openpype.lib.plugin_tools import parse_json -def headless_publish(log, close_plugin_name=None, is_test=False): - """Runs publish in a opened host with a context and closes Python process. - - Host is being closed via ClosePS pyblish plugin which triggers 'exit' - method in ConsoleTrayApp. - """ - if not is_test: - dbcon = get_webpublish_conn() - _id = os.environ.get("BATCH_LOG_ID") - if not _id: - log.warning("Unable to store log records, " - "batch will be unfinished!") - return - - publish_and_log(dbcon, _id, log, close_plugin_name) - else: - publish(log, close_plugin_name) - - def get_webpublish_conn(): """Get connection to OP 'webpublishes' collection.""" mongo_client = OpenPypeMongoConnection.get_mongo_client() @@ -56,33 +37,6 @@ def start_webpublish_log(dbcon, batch_id, user): }).inserted_id -def publish(log, close_plugin_name=None): - """Loops through all plugins, logs to console. Used for tests. - - Args: - log (OpenPypeLogger) - close_plugin_name (str): name of plugin with responsibility to - close host app - """ - # Error exit as soon as any error occurs. - error_format = "Failed {plugin.__name__}: {error} -- {error.traceback}" - - close_plugin = _get_close_plugin(close_plugin_name, log) - - for result in pyblish.util.publish_iter(): - for record in result["records"]: - log.info("{}: {}".format( - result["plugin"].label, record.msg)) - - if result["error"]: - log.error(error_format.format(**result)) - uninstall() - if close_plugin: # close host app explicitly after error - context = pyblish.api.Context() - close_plugin().process(context) - sys.exit(1) - - def publish_and_log(dbcon, _id, log, close_plugin_name=None): """Loops through all plugins, logs ok and fails into OP DB. diff --git a/openpype/settings/defaults/system_settings/applications.json b/openpype/settings/defaults/system_settings/applications.json index 4e502010366..c730e02984d 100644 --- a/openpype/settings/defaults/system_settings/applications.json +++ b/openpype/settings/defaults/system_settings/applications.json @@ -1098,23 +1098,6 @@ "linux": [] }, "environment": {} - }, - "2022": { - "enabled": true, - "variant_label": "2022", - "executables": { - "windows": [ - "C:\\Program Files\\Adobe\\Adobe After Effects 2022\\Support Files\\AfterFX.exe" - ], - "darwin": [], - "linux": [] - }, - "arguments": { - "windows": [], - "darwin": [], - "linux": [] - }, - "environment": {} } } }, diff --git a/tests/integration/hosts/aftereffects/test_publish_in_aftereffects.py b/tests/integration/hosts/aftereffects/test_publish_in_aftereffects.py deleted file mode 100644 index d4e88dfd4c5..00000000000 --- a/tests/integration/hosts/aftereffects/test_publish_in_aftereffects.py +++ /dev/null @@ -1,96 +0,0 @@ -import pytest -import os -import shutil - -from tests.lib.testing_classes import PublishTest - - -class TestPublishInAfterEffects(PublishTest): - """Basic test case for publishing in AfterEffects - - Uses generic TestCase to prepare fixtures for test data, testing DBs, - env vars. - - Opens AfterEffects, run publish on prepared workile. - - Then checks content of DB (if subset, version, representations were - created. - Checks tmp folder if all expected files were published. - - """ - PERSIST = True - - TEST_FILES = [ - ("1qsrq6OJWVpOeXE2LTWrdbsLqEVu155Uf", - "test_aftereffects_publish.zip", - "") - ] - - APP = "aftereffects" - APP_VARIANT = "2021" - - APP_NAME = "{}/{}".format(APP, APP_VARIANT) - - TIMEOUT = 120 # publish timeout - - @pytest.fixture(scope="module") - def last_workfile_path(self, download_test_data): - """Get last_workfile_path from source data. - - Maya expects workfile in proper folder, so copy is done first. - """ - src_path = os.path.join(download_test_data, - "input", - "workfile", - "test_project_test_asset_TestTask_v001.aep") - dest_folder = os.path.join(download_test_data, - self.PROJECT, - self.ASSET, - "work", - self.TASK) - os.makedirs(dest_folder) - dest_path = os.path.join(dest_folder, - "test_project_test_asset_TestTask_v001.aep") - shutil.copy(src_path, dest_path) - - yield dest_path - - @pytest.fixture(scope="module") - def startup_scripts(self, monkeypatch_session, download_test_data): - """Points AfterEffects to userSetup file from input data""" - pass - - def test_db_asserts(self, dbcon, publish_finished): - """Host and input data dependent expected results in DB.""" - print("test_db_asserts") - assert 5 == dbcon.count_documents({"type": "version"}), \ - "Not expected no of versions" - - assert 0 == dbcon.count_documents({"type": "version", - "name": {"$ne": 1}}), \ - "Only versions with 1 expected" - - assert 1 == dbcon.count_documents({"type": "subset", - "name": "modelMain"}), \ - "modelMain subset must be present" - - assert 1 == dbcon.count_documents({"type": "subset", - "name": "workfileTest_task"}), \ - "workfileTest_task subset must be present" - - assert 11 == dbcon.count_documents({"type": "representation"}), \ - "Not expected no of representations" - - assert 2 == dbcon.count_documents({"type": "representation", - "context.subset": "modelMain", - "context.ext": "abc"}), \ - "Not expected no of representations with ext 'abc'" - - assert 2 == dbcon.count_documents({"type": "representation", - "context.subset": "modelMain", - "context.ext": "ma"}), \ - "Not expected no of representations with ext 'abc'" - - -if __name__ == "__main__": - test_case = TestPublishInAfterEffects() From 486966efbc7daddefb6e264ec05a5718f8fce395 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 Dec 2021 10:16:05 +0100 Subject: [PATCH 065/492] Reverting change to avalon-core --- repos/avalon-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repos/avalon-core b/repos/avalon-core index 1d94a75ddf3..9499f6517a1 160000 --- a/repos/avalon-core +++ b/repos/avalon-core @@ -1 +1 @@ -Subproject commit 1d94a75ddf37a354955a9e7f4bb8695187f3e0ed +Subproject commit 9499f6517a1ff2d3bf94c5d34c0aece146734760 From f9a1445a748f15a6592ad06ec34b748af1fddad0 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 Dec 2021 10:28:41 +0100 Subject: [PATCH 066/492] OP-2019 - revert to develop version --- .../integration/hosts/photoshop/test_publish_in_photoshop.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/hosts/photoshop/test_publish_in_photoshop.py b/tests/integration/hosts/photoshop/test_publish_in_photoshop.py index b634d422f34..396468a9661 100644 --- a/tests/integration/hosts/photoshop/test_publish_in_photoshop.py +++ b/tests/integration/hosts/photoshop/test_publish_in_photoshop.py @@ -25,7 +25,7 @@ class TestPublishInPhotoshop(PublishTest): ] APP = "photoshop" - APP_VARIANT = "2021" + APP_VARIANT = "2020" APP_NAME = "{}/{}".format(APP, APP_VARIANT) @@ -56,7 +56,7 @@ def last_workfile_path(self, download_test_data): @pytest.fixture(scope="module") def startup_scripts(self, monkeypatch_session, download_test_data): """Points Maya to userSetup file from input data""" - pass + os.environ["IS_HEADLESS"] = "true" def test_db_asserts(self, dbcon, publish_finished): """Host and input data dependent expected results in DB.""" From 477d177bd9f9939f3c74c63ac47be5b68d52d372 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 Dec 2021 11:42:22 +0100 Subject: [PATCH 067/492] OP-2019 - fixes for PS test class --- .../photoshop/test_publish_in_photoshop.py | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/tests/integration/hosts/photoshop/test_publish_in_photoshop.py b/tests/integration/hosts/photoshop/test_publish_in_photoshop.py index 396468a9661..4754f604864 100644 --- a/tests/integration/hosts/photoshop/test_publish_in_photoshop.py +++ b/tests/integration/hosts/photoshop/test_publish_in_photoshop.py @@ -11,7 +11,12 @@ class TestPublishInPhotoshop(PublishTest): Uses generic TestCase to prepare fixtures for test data, testing DBs, env vars. - Opens Maya, run publish on prepared workile. + Opens Photoshop, run publish on prepared workile. + + Test zip file sets 3 required env vars: + - HEADLESS_PUBLISH - this triggers publish immediately app is open + - IS_TEST - this differentiate between regular webpublish + - PYBLISH_TARGETS Then checks content of DB (if subset, version, representations were created. @@ -21,11 +26,11 @@ class TestPublishInPhotoshop(PublishTest): PERSIST = True TEST_FILES = [ - ("1Bciy2pCwMKl1UIpxuPnlX_LHMo_Xkq0K", "test_photoshop_publish.zip", "") + ("1zD2v5cBgkyOm_xIgKz3WKn8aFB_j8qC-", "test_photoshop_publish.zip", "") ] APP = "photoshop" - APP_VARIANT = "2020" + APP_VARIANT = "2021" APP_NAME = "{}/{}".format(APP, APP_VARIANT) @@ -56,12 +61,12 @@ def last_workfile_path(self, download_test_data): @pytest.fixture(scope="module") def startup_scripts(self, monkeypatch_session, download_test_data): """Points Maya to userSetup file from input data""" - os.environ["IS_HEADLESS"] = "true" + pass def test_db_asserts(self, dbcon, publish_finished): """Host and input data dependent expected results in DB.""" print("test_db_asserts") - assert 5 == dbcon.count_documents({"type": "version"}), \ + assert 3 == dbcon.count_documents({"type": "version"}), \ "Not expected no of versions" assert 0 == dbcon.count_documents({"type": "version", @@ -69,25 +74,21 @@ def test_db_asserts(self, dbcon, publish_finished): "Only versions with 1 expected" assert 1 == dbcon.count_documents({"type": "subset", - "name": "modelMain"}), \ + "name": "imageMainBackgroundcopy"} + ), \ "modelMain subset must be present" assert 1 == dbcon.count_documents({"type": "subset", - "name": "workfileTest_task"}), \ + "name": "workfileTesttask"}), \ "workfileTest_task subset must be present" - assert 11 == dbcon.count_documents({"type": "representation"}), \ + assert 6 == dbcon.count_documents({"type": "representation"}), \ "Not expected no of representations" - assert 2 == dbcon.count_documents({"type": "representation", - "context.subset": "modelMain", - "context.ext": "abc"}), \ - "Not expected no of representations with ext 'abc'" - - assert 2 == dbcon.count_documents({"type": "representation", - "context.subset": "modelMain", - "context.ext": "ma"}), \ - "Not expected no of representations with ext 'abc'" + assert 1 == dbcon.count_documents({"type": "representation", + "context.subset": "imageMainBackgroundcopy", # noqa: E501 + "context.ext": "png"}), \ + "Not expected no of representations with ext 'png'" if __name__ == "__main__": From 383307d88803404c1bac74366797b5a23e2fc5e0 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Fri, 3 Dec 2021 13:51:03 +0100 Subject: [PATCH 068/492] tweak tests and add get latest version --- igniter/bootstrap_repos.py | 95 ++++++++-------------- start.py | 1 - tests/unit/igniter/test_bootstrap_repos.py | 7 +- 3 files changed, 40 insertions(+), 63 deletions(-) diff --git a/igniter/bootstrap_repos.py b/igniter/bootstrap_repos.py index b0f3b482acc..ca64d193c76 100644 --- a/igniter/bootstrap_repos.py +++ b/igniter/bootstrap_repos.py @@ -401,16 +401,16 @@ def get_available_versions( def get_versions_from_directory(openpype_dir: Path) -> List: """Get all detected OpenPype versions in directory. - Args: - openpype_dir (Path): Directory to scan. + Args: + openpype_dir (Path): Directory to scan. - Returns: - list of OpenPypeVersion + Returns: + list of OpenPypeVersion - Throws: - ValueError: if invalid path is specified. + Throws: + ValueError: if invalid path is specified. - """ + """ if not openpype_dir.exists() and not openpype_dir.is_dir(): raise ValueError("specified directory is invalid") @@ -442,6 +442,26 @@ def get_versions_from_directory(openpype_dir: Path) -> List: return sorted(_openpype_versions) + @staticmethod + def get_latest_version( + staging: bool = False, local: bool = False) -> OpenPypeVersion: + """Get latest available version. + + This is utility version to get latest version from all found. + + Args: + staging (bool, optional): List staging versions if True. + local (bool, optional): List only local versions. + + See also: + OpenPypeVersion.get_available_versions() + + """ + openpype_versions = OpenPypeVersion.get_available_versions( + staging, local) + + return openpype_versions[-1] + class BootstrapRepos: """Class for bootstrapping local OpenPype installation. @@ -944,66 +964,23 @@ def add_paths_from_directory(directory: Path) -> None: os.environ["PYTHONPATH"] = os.pathsep.join(paths) + @staticmethod def find_openpype( - self, openpype_path: Union[Path, str] = None, staging: bool = False, include_zips: bool = False) -> Union[List[OpenPypeVersion], None]: - """Get ordered dict of detected OpenPype version. - - Resolution order for OpenPype is following: - - 1) First we test for ``OPENPYPE_PATH`` environment variable - 2) We try to find ``openPypePath`` in registry setting - 3) We use user data directory - Args: - openpype_path (Path or str, optional): Try to find OpenPype on - the given path or url. - staging (bool, optional): Filter only staging version, skip them - otherwise. - include_zips (bool, optional): If set True it will try to find - OpenPype in zip files in given directory. - - Returns: - dict of Path: Dictionary of detected OpenPype version. - Key is version, value is path to zip file. - - None: if OpenPype is not found. - - Todo: - implement git/url support as OpenPype location, so it would be - possible to enter git url, OpenPype would check it out and if it is - ok install it as normal version. - - """ - if openpype_path and not isinstance(openpype_path, Path): - raise NotImplementedError( - ("Finding OpenPype in non-filesystem locations is" - " not implemented yet.")) - - dir_to_search = self.data_dir - user_versions = self.get_openpype_versions(self.data_dir, staging) - # if we have openpype_path specified, search only there. if openpype_path: - dir_to_search = openpype_path - else: - if os.getenv("OPENPYPE_PATH"): - if Path(os.getenv("OPENPYPE_PATH")).exists(): - dir_to_search = Path(os.getenv("OPENPYPE_PATH")) - else: - try: - registry_dir = Path( - str(self.registry.get_item("openPypePath"))) - if registry_dir.exists(): - dir_to_search = registry_dir + openpype_versions = OpenPypeVersion.get_versions_from_directory( + openpype_path) + # filter out staging - except ValueError: - # nothing found in registry, we'll use data dir - pass + openpype_versions = [ + v for v in openpype_versions if v.is_staging() == staging + ] - openpype_versions = self.get_openpype_versions(dir_to_search, staging) - openpype_versions += user_versions + else: + openpype_versions = OpenPypeVersion.get_available_versions(staging) # remove zip file version if needed. if not include_zips: diff --git a/start.py b/start.py index 0f7e82071d7..05b7da6308e 100644 --- a/start.py +++ b/start.py @@ -966,7 +966,6 @@ def boot(): ) sys.exit(1) - if not openpype_path: _print("*** Cannot get OpenPype path from database.") diff --git a/tests/unit/igniter/test_bootstrap_repos.py b/tests/unit/igniter/test_bootstrap_repos.py index d6e861c2627..65cd5a23997 100644 --- a/tests/unit/igniter/test_bootstrap_repos.py +++ b/tests/unit/igniter/test_bootstrap_repos.py @@ -140,9 +140,10 @@ def test_search_string_for_openpype_version(printer): ] for ver_string in strings: printer(f"testing {ver_string[0]} should be {ver_string[1]}") - assert OpenPypeVersion.version_in_str(ver_string[0]) == \ - ver_string[1] - + assert isinstance( + OpenPypeVersion.version_in_str(ver_string[0]), + OpenPypeVersion if ver_string[1] else type(None) + ) @pytest.mark.slow def test_install_live_repos(fix_bootstrap, printer, monkeypatch, pytestconfig): From d5b2127bcc04bbc5abab0de8c670e600c024cae2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 3 Dec 2021 13:52:56 +0100 Subject: [PATCH 069/492] moved avalon apps module --- openpype/modules/{default_modules => }/avalon_apps/__init__.py | 0 openpype/modules/{default_modules => }/avalon_apps/avalon_app.py | 0 openpype/modules/{default_modules => }/avalon_apps/rest_api.py | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename openpype/modules/{default_modules => }/avalon_apps/__init__.py (100%) rename openpype/modules/{default_modules => }/avalon_apps/avalon_app.py (100%) rename openpype/modules/{default_modules => }/avalon_apps/rest_api.py (100%) diff --git a/openpype/modules/default_modules/avalon_apps/__init__.py b/openpype/modules/avalon_apps/__init__.py similarity index 100% rename from openpype/modules/default_modules/avalon_apps/__init__.py rename to openpype/modules/avalon_apps/__init__.py diff --git a/openpype/modules/default_modules/avalon_apps/avalon_app.py b/openpype/modules/avalon_apps/avalon_app.py similarity index 100% rename from openpype/modules/default_modules/avalon_apps/avalon_app.py rename to openpype/modules/avalon_apps/avalon_app.py diff --git a/openpype/modules/default_modules/avalon_apps/rest_api.py b/openpype/modules/avalon_apps/rest_api.py similarity index 100% rename from openpype/modules/default_modules/avalon_apps/rest_api.py rename to openpype/modules/avalon_apps/rest_api.py From 343159d5af8b1a77e8c7e95fe685c78a39e0ed88 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 3 Dec 2021 14:12:32 +0100 Subject: [PATCH 070/492] define constant with default modules --- openpype/modules/base.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/openpype/modules/base.py b/openpype/modules/base.py index e300dd08063..29c1c6fd5a6 100644 --- a/openpype/modules/base.py +++ b/openpype/modules/base.py @@ -29,6 +29,21 @@ from openpype.lib import PypeLogger +DEFAULT_OPENPYPE_MODULES = ( + "avalon_apps", + "clockify", + "log_viewer", + "muster", + "python_console_interpreter", + "slack", + "webserver", + "launcher_action", + "project_manager_action", + "settings_action", + "standalonepublish_action", +) + + # Inherit from `object` for Python 2 hosts class _ModuleClass(object): """Fake module class for storing OpenPype modules. @@ -272,18 +287,7 @@ def _load_modules(): log = PypeLogger.get_logger("ModulesLoader") # Import default modules imported from 'openpype.modules' - for default_module_name in ( - "webserver", - "python_console_interpreter", - "slack", - "log_viewer", - "muster", - "clockify", - "settings_action", - "launcher_action", - "project_manager_action", - "standalonepublish_action", - ): + for default_module_name in DEFAULT_OPENPYPE_MODULES: try: default_module = __import__( "openpype.modules.{}".format(default_module_name), From 10d959dd03c176c25fed674b09922eaf44302732 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 3 Dec 2021 14:13:16 +0100 Subject: [PATCH 071/492] modified and fixed import of default modules into 'openpype_modules' --- openpype/modules/base.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/modules/base.py b/openpype/modules/base.py index 29c1c6fd5a6..a1df3cfd14d 100644 --- a/openpype/modules/base.py +++ b/openpype/modules/base.py @@ -289,10 +289,10 @@ def _load_modules(): # Import default modules imported from 'openpype.modules' for default_module_name in DEFAULT_OPENPYPE_MODULES: try: - default_module = __import__( - "openpype.modules.{}".format(default_module_name), - fromlist=("", ) - ) + import_str = "openpype.modules.{}".format(default_module_name) + new_import_str = "{}.{}".format(modules_key, default_module_name) + default_module = __import__(import_str, fromlist=("", )) + sys.modules[new_import_str] = default_module setattr(openpype_modules, default_module_name, default_module) except Exception: From 2e753e1ca2aac3cc0e43e82cbe0bc31a032a1f43 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 Dec 2021 14:39:49 +0100 Subject: [PATCH 072/492] OP-2019 - fixes for db_asserts --- .../test_publish_in_aftereffects.py | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/tests/integration/hosts/aftereffects/test_publish_in_aftereffects.py b/tests/integration/hosts/aftereffects/test_publish_in_aftereffects.py index d4e88dfd4c5..3d1fa8f804d 100644 --- a/tests/integration/hosts/aftereffects/test_publish_in_aftereffects.py +++ b/tests/integration/hosts/aftereffects/test_publish_in_aftereffects.py @@ -13,6 +13,11 @@ class TestPublishInAfterEffects(PublishTest): Opens AfterEffects, run publish on prepared workile. + Test zip file sets 3 required env vars: + - HEADLESS_PUBLISH - this triggers publish immediately app is open + - IS_TEST - this differentiate between regular webpublish + - PYBLISH_TARGETS + Then checks content of DB (if subset, version, representations were created. Checks tmp folder if all expected files were published. @@ -21,13 +26,13 @@ class TestPublishInAfterEffects(PublishTest): PERSIST = True TEST_FILES = [ - ("1qsrq6OJWVpOeXE2LTWrdbsLqEVu155Uf", + ("1c8261CmHwyMgS-g7S4xL5epAp0jCBmhf", "test_aftereffects_publish.zip", "") ] APP = "aftereffects" - APP_VARIANT = "2021" + APP_VARIANT = "2022" APP_NAME = "{}/{}".format(APP, APP_VARIANT) @@ -63,7 +68,7 @@ def startup_scripts(self, monkeypatch_session, download_test_data): def test_db_asserts(self, dbcon, publish_finished): """Host and input data dependent expected results in DB.""" print("test_db_asserts") - assert 5 == dbcon.count_documents({"type": "version"}), \ + assert 3 == dbcon.count_documents({"type": "version"}), \ "Not expected no of versions" assert 0 == dbcon.count_documents({"type": "version", @@ -71,25 +76,25 @@ def test_db_asserts(self, dbcon, publish_finished): "Only versions with 1 expected" assert 1 == dbcon.count_documents({"type": "subset", - "name": "modelMain"}), \ + "name": "imageMainBackgroundcopy" + }), \ "modelMain subset must be present" assert 1 == dbcon.count_documents({"type": "subset", - "name": "workfileTest_task"}), \ - "workfileTest_task subset must be present" + "name": "workfileTesttask"}), \ + "workfileTesttask subset must be present" - assert 11 == dbcon.count_documents({"type": "representation"}), \ - "Not expected no of representations" + assert 1 == dbcon.count_documents({"type": "subset", + "name": "reviewTesttask"}), \ + "reviewTesttask subset must be present" - assert 2 == dbcon.count_documents({"type": "representation", - "context.subset": "modelMain", - "context.ext": "abc"}), \ - "Not expected no of representations with ext 'abc'" + assert 6 == dbcon.count_documents({"type": "representation"}), \ + "Not expected no of representations" - assert 2 == dbcon.count_documents({"type": "representation", - "context.subset": "modelMain", - "context.ext": "ma"}), \ - "Not expected no of representations with ext 'abc'" + assert 1 == dbcon.count_documents({"type": "representation", + "context.subset": "imageMainBackgroundcopy", #noqa E501 + "context.ext": "png"}), \ + "Not expected no of representations with ext 'png'" if __name__ == "__main__": From 68b5078ae147130d3d10e4208a3e3ad7d9a20887 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 Dec 2021 15:37:00 +0100 Subject: [PATCH 073/492] OP-2042 - always capture stdout in pytest Previously it was printed only in case of failure --- openpype/pype_commands.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/pype_commands.py b/openpype/pype_commands.py index 7f6e5f1c0cf..ce1a9718b37 100644 --- a/openpype/pype_commands.py +++ b/openpype/pype_commands.py @@ -365,9 +365,10 @@ def run_tests(self, folder, mark, pyargs): if pyargs: pyargs_str = "--pyargs {}".format(pyargs) - depr_str = "--disable-pytest-warnings" + # disable warnings and show captured stdout even if success + args_str = "--disable-pytest-warnings -rP" - cmd = "pytest {} {} {} {}".format(depr_str, folder, + cmd = "pytest {} {} {} {}".format(args_str, folder, mark_str, pyargs_str) print("Running {}".format(cmd)) subprocess.run(cmd) From d55d996f9c58030c86de4eb158837ea289d834d4 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 Dec 2021 15:39:43 +0100 Subject: [PATCH 074/492] OP-2042 - added functionality to reuse existing folder for testdata --- .../hosts/nuke/test_publish_in_nuke.py | 18 +++----- tests/lib/testing_classes.py | 42 +++++++++++-------- 2 files changed, 29 insertions(+), 31 deletions(-) diff --git a/tests/integration/hosts/nuke/test_publish_in_nuke.py b/tests/integration/hosts/nuke/test_publish_in_nuke.py index 6dc880d757a..483e9fef985 100644 --- a/tests/integration/hosts/nuke/test_publish_in_nuke.py +++ b/tests/integration/hosts/nuke/test_publish_in_nuke.py @@ -25,7 +25,7 @@ class TestPublishInNuke(PublishTest): {OPENPYPE_ROOT}/.venv/Scripts/python.exe {OPENPYPE_ROOT}/start.py runtests ../tests/integration/hosts/nuke # noqa: E501 """ - PERSIST = True # True - keep test_db, test_openpype, outputted test files + PERSIST = False # True - keep test_db, test_openpype, outputted test files TEST_FILES = [ ("1SUurHj2aiQ21ZIMJfGVBI2KjR8kIjBGI", "test_Nuke_publish.zip", "") @@ -38,11 +38,12 @@ class TestPublishInNuke(PublishTest): TIMEOUT = 120 # publish timeout + TEST_DATA_FOLDER = None # provide existing folder with test data + @pytest.fixture(scope="module") def last_workfile_path(self, download_test_data): """Get last_workfile_path from source data. - Maya expects workfile in proper folder, so copy is done first. """ log.info("log last_workfile_path") src_path = os.path.join( @@ -50,17 +51,8 @@ def last_workfile_path(self, download_test_data): "input", "workfile", "test_project_test_asset_CompositingInNuke_v001.nk") - dest_folder = os.path.join(download_test_data, - self.PROJECT, - self.ASSET, - "work", - self.TASK) - os.makedirs(dest_folder) - dest_path = os.path.join( - dest_folder, "test_project_test_asset_CompositingInNuke_v001.nk") - shutil.copy(src_path, dest_path) - - yield dest_path + + yield src_path @pytest.fixture(scope="module") def startup_scripts(self, monkeypatch_session, download_test_data): diff --git a/tests/lib/testing_classes.py b/tests/lib/testing_classes.py index 59d4abb3aa8..aa8ef3caabc 100644 --- a/tests/lib/testing_classes.py +++ b/tests/lib/testing_classes.py @@ -45,6 +45,8 @@ class ModuleUnitTest(BaseTest): ASSET = "test_asset" TASK = "test_task" + TEST_DATA_FOLDER = None + @pytest.fixture(scope='session') def monkeypatch_session(self): """Monkeypatch couldn't be used with module or session fixtures.""" @@ -55,24 +57,28 @@ def monkeypatch_session(self): @pytest.fixture(scope="module") def download_test_data(self): - tmpdir = tempfile.mkdtemp() - for test_file in self.TEST_FILES: - file_id, file_name, md5 = test_file - - f_name, ext = os.path.splitext(file_name) - - RemoteFileHandler.download_file_from_google_drive(file_id, - str(tmpdir), - file_name) - - if ext.lstrip('.') in RemoteFileHandler.IMPLEMENTED_ZIP_FORMATS: - RemoteFileHandler.unzip(os.path.join(tmpdir, file_name)) - print("Temporary folder created:: {}".format(tmpdir)) - yield tmpdir - - if not self.PERSIST: - print("Removing {}".format(tmpdir)) - shutil.rmtree(tmpdir) + if self.TEST_DATA_FOLDER: + print("Using existing folder {}".format(self.TEST_DATA_FOLDER)) + yield self.TEST_DATA_FOLDER + else: + tmpdir = tempfile.mkdtemp() + for test_file in self.TEST_FILES: + file_id, file_name, md5 = test_file + + f_name, ext = os.path.splitext(file_name) + + RemoteFileHandler.download_file_from_google_drive(file_id, + str(tmpdir), + file_name) + + if ext.lstrip('.') in RemoteFileHandler.IMPLEMENTED_ZIP_FORMATS: #noqa E501 + RemoteFileHandler.unzip(os.path.join(tmpdir, file_name)) + print("Temporary folder created:: {}".format(tmpdir)) + yield tmpdir + + if not self.PERSIST: + print("Removing {}".format(tmpdir)) + shutil.rmtree(tmpdir) @pytest.fixture(scope="module") def env_var(self, monkeypatch_session, download_test_data): From 88218caf2869d8452b734ed4712fa224a540b0c6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 3 Dec 2021 15:56:11 +0100 Subject: [PATCH 075/492] store OpenPypeVersion to sys.modules --- igniter/__init__.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/igniter/__init__.py b/igniter/__init__.py index defd45e233a..bbc3dbfc88c 100644 --- a/igniter/__init__.py +++ b/igniter/__init__.py @@ -6,9 +6,15 @@ os.chdir(os.path.dirname(__file__)) # for override sys.path in Deadline -from .bootstrap_repos import BootstrapRepos +from .bootstrap_repos import ( + BootstrapRepos, + OpenPypeVersion +) from .version import __version__ as version +if "OpenPypeVersion" not in sys.modules: + sys.modules["OpenPypeVersion"] = OpenPypeVersion + def open_dialog(): """Show Igniter dialog.""" From 2bfdc5c3e970b1747e39b3ed16f439fe4d0e59d2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 3 Dec 2021 16:05:36 +0100 Subject: [PATCH 076/492] create new enum entities used to determine openpype version --- openpype/settings/entities/__init__.py | 6 ++- openpype/settings/entities/enum_entity.py | 52 +++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/openpype/settings/entities/__init__.py b/openpype/settings/entities/__init__.py index ccf2a5993ec..e4a13b80536 100644 --- a/openpype/settings/entities/__init__.py +++ b/openpype/settings/entities/__init__.py @@ -106,7 +106,9 @@ ToolsEnumEntity, TaskTypeEnumEntity, DeadlineUrlEnumEntity, - AnatomyTemplatesEnumEntity + AnatomyTemplatesEnumEntity, + ProductionVersionsEnumEntity, + StagingVersionsEnumEntity ) from .list_entity import ListEntity @@ -169,6 +171,8 @@ "TaskTypeEnumEntity", "DeadlineUrlEnumEntity", "AnatomyTemplatesEnumEntity", + "ProductionVersionsEnumEntity", + "StagingVersionsEnumEntity", "ListEntity", diff --git a/openpype/settings/entities/enum_entity.py b/openpype/settings/entities/enum_entity.py index ab3cebbd421..5f0cbb12615 100644 --- a/openpype/settings/entities/enum_entity.py +++ b/openpype/settings/entities/enum_entity.py @@ -1,4 +1,5 @@ import copy +import sys from .input_entities import InputEntity from .exceptions import EntitySchemaError from .lib import ( @@ -564,3 +565,54 @@ def set_override_state(self, *args, **kwargs): self.enum_items, self.valid_keys = self._get_enum_values() if self._current_value not in self.valid_keys: self._current_value = self.value_on_not_set + + +class _OpenPypeVersionEnum(BaseEnumEntity): + def _item_initialization(self): + self.multiselection = False + self.valid_value_types = (STRING_TYPE, ) + + self.value_on_not_set = "" + items = [ + {"": "Latest"} + ] + items.extend(self._get_openpype_versions()) + + self.enum_items = items + self.valid_keys = { + tuple(item.keys())[0] + for item in items + } + + def _get_openpype_versions(self): + return [] + + +class ProductionVersionsEnumEntity(_OpenPypeVersionEnum): + schema_types = ["production-versions-enum"] + + def _get_openpype_versions(self): + items = [] + if "OpenPypeVersion" in sys.modules: + OpenPypeVersion = sys.modules["OpenPypeVersion"] + versions = OpenPypeVersion.get_available_versions( + staging=False, local=False + ) + for item in versions: + items.append({item: item}) + return items + + +class StagingVersionsEnumEntity(_OpenPypeVersionEnum): + schema_types = ["staging-versions-enum"] + + def _get_openpype_versions(self): + items = [] + if "OpenPypeVersion" in sys.modules: + OpenPypeVersion = sys.modules["OpenPypeVersion"] + versions = OpenPypeVersion.get_available_versions( + staging=False, local=False + ) + for item in versions: + items.append({item: item}) + return items From 27b1be9279152b7fde4c3fbd5bb9d2d507cfc299 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 3 Dec 2021 16:05:49 +0100 Subject: [PATCH 077/492] use new enities in settings --- .../settings/defaults/system_settings/general.json | 2 ++ .../schemas/system_schema/schema_general.json | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/openpype/settings/defaults/system_settings/general.json b/openpype/settings/defaults/system_settings/general.json index f54e8b2b16b..a07152eaf87 100644 --- a/openpype/settings/defaults/system_settings/general.json +++ b/openpype/settings/defaults/system_settings/general.json @@ -2,6 +2,8 @@ "studio_name": "Studio name", "studio_code": "stu", "admin_password": "", + "production_version": "", + "staging_version": "", "environment": { "__environment_keys__": { "global": [] diff --git a/openpype/settings/entities/schemas/system_schema/schema_general.json b/openpype/settings/entities/schemas/system_schema/schema_general.json index 51a58a6e274..d548a22c975 100644 --- a/openpype/settings/entities/schemas/system_schema/schema_general.json +++ b/openpype/settings/entities/schemas/system_schema/schema_general.json @@ -30,6 +30,19 @@ { "type": "splitter" }, + { + "type": "production-versions-enum", + "key": "production_version", + "label": "Production version" + }, + { + "type": "staging-versions-enum", + "key": "staging_version", + "label": "Staging version" + }, + { + "type": "splitter" + }, { "key": "environment", "label": "Environment", From ab8dacd0f8cc4e7f578695a6a5159472876ea1aa Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 3 Dec 2021 16:06:04 +0100 Subject: [PATCH 078/492] added "production_version" and "staging_version" to global settings --- openpype/settings/handlers.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/openpype/settings/handlers.py b/openpype/settings/handlers.py index c59e2bc5426..51e390bb6df 100644 --- a/openpype/settings/handlers.py +++ b/openpype/settings/handlers.py @@ -168,7 +168,13 @@ def is_outdated(self): class MongoSettingsHandler(SettingsHandler): """Settings handler that use mongo for storing and loading of settings.""" - global_general_keys = ("openpype_path", "admin_password", "disk_mapping") + global_general_keys = ( + "openpype_path", + "admin_password", + "disk_mapping", + "production_version", + "staging_version" + ) def __init__(self): # Get mongo connection From d0ada90e44a4746f2dcb154a0e46d0e77998c958 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 Dec 2021 16:18:07 +0100 Subject: [PATCH 079/492] OP-2042 - added functionality to implicit choose variant If APP_VARIANT is empty it looks for latest installed variant of an application --- .../hosts/maya/test_publish_in_maya.py | 7 +++---- .../hosts/nuke/test_publish_in_nuke.py | 5 ++--- .../photoshop/test_publish_in_photoshop.py | 7 +++---- tests/lib/testing_classes.py | 19 +++++++++++++++---- 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/tests/integration/hosts/maya/test_publish_in_maya.py b/tests/integration/hosts/maya/test_publish_in_maya.py index b53b26f66dd..687e6fbc6ea 100644 --- a/tests/integration/hosts/maya/test_publish_in_maya.py +++ b/tests/integration/hosts/maya/test_publish_in_maya.py @@ -26,16 +26,15 @@ class TestPublishInMaya(PublishTest): {OPENPYPE_ROOT}/.venv/Scripts/python.exe {OPENPYPE_ROOT}/start.py runtests ../tests/integration/hosts/maya # noqa: E501 """ - PERSIST = True + PERSIST = False TEST_FILES = [ ("1BTSIIULJTuDc8VvXseuiJV_fL6-Bu7FP", "test_maya_publish.zip", "") ] APP = "maya" - APP_VARIANT = "2019" - - APP_NAME = "{}/{}".format(APP, APP_VARIANT) + # keep empty to locate latest installed variant or explicit + APP_VARIANT = "" TIMEOUT = 120 # publish timeout diff --git a/tests/integration/hosts/nuke/test_publish_in_nuke.py b/tests/integration/hosts/nuke/test_publish_in_nuke.py index 483e9fef985..14a79fdf3d2 100644 --- a/tests/integration/hosts/nuke/test_publish_in_nuke.py +++ b/tests/integration/hosts/nuke/test_publish_in_nuke.py @@ -32,9 +32,8 @@ class TestPublishInNuke(PublishTest): ] APP = "nuke" - APP_VARIANT = "12-2" - - APP_NAME = "{}/{}".format(APP, APP_VARIANT) + # keep empty to locate latest installed variant or explicit + APP_VARIANT = "" TIMEOUT = 120 # publish timeout diff --git a/tests/integration/hosts/photoshop/test_publish_in_photoshop.py b/tests/integration/hosts/photoshop/test_publish_in_photoshop.py index 43e03b2cc3b..c7f23994949 100644 --- a/tests/integration/hosts/photoshop/test_publish_in_photoshop.py +++ b/tests/integration/hosts/photoshop/test_publish_in_photoshop.py @@ -24,16 +24,15 @@ class TestPublishInPhotoshop(PublishTest): {OPENPYPE_ROOT}/.venv/Scripts/python.exe {OPENPYPE_ROOT}/start.py runtests ../tests/integration/hosts/photoshop # noqa: E501 """ - PERSIST = True + PERSIST = False TEST_FILES = [ ("1zD2v5cBgkyOm_xIgKz3WKn8aFB_j8qC-", "test_photoshop_publish.zip", "") ] APP = "photoshop" - APP_VARIANT = "2021" - - APP_NAME = "{}/{}".format(APP, APP_VARIANT) + # keep empty to locate latest installed variant or explicit + APP_VARIANT = "" TIMEOUT = 120 # publish timeout diff --git a/tests/lib/testing_classes.py b/tests/lib/testing_classes.py index aa8ef3caabc..bf1c490ecdf 100644 --- a/tests/lib/testing_classes.py +++ b/tests/lib/testing_classes.py @@ -11,6 +11,8 @@ from tests.lib.db_handler import DBHandler from tests.lib.file_handler import RemoteFileHandler +from openpype.lib.remote_publish import find_variant_key + class BaseTest: """Empty base test class""" @@ -173,12 +175,15 @@ class PublishTest(ModuleUnitTest): """ APP = "" - APP_VARIANT = "" - - APP_NAME = "{}/{}".format(APP, APP_VARIANT) + APP_VARIANT = "" # keep empty to locate latest installed variant TIMEOUT = 120 # publish timeout + @property + def app_name(self): + if self.APP_VARIANT: + return "{}/{}".format(self.APP, self.APP_VARIANT) + @pytest.fixture(scope="module") def last_workfile_path(self, download_test_data): raise NotImplementedError @@ -224,7 +229,13 @@ def launched_app(self, dbcon, download_test_data, last_workfile_path, "task_name": self.TASK } - yield application_manager.launch(self.APP_NAME, **data) + variant = self.APP_VARIANT + if not variant: + variant = find_variant_key(application_manager, self.APP) + + app_name = "{}/{}".format(self.APP, variant) + + yield application_manager.launch(app_name, **data) @pytest.fixture(scope="module") def publish_finished(self, dbcon, launched_app, download_test_data): From b61b3634b289406469e08ab3a9617860ef979840 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 Dec 2021 16:20:31 +0100 Subject: [PATCH 080/492] OP-2042 - Hound --- tests/integration/hosts/nuke/test_publish_in_nuke.py | 1 - tests/lib/testing_classes.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/integration/hosts/nuke/test_publish_in_nuke.py b/tests/integration/hosts/nuke/test_publish_in_nuke.py index 14a79fdf3d2..fe1745299d9 100644 --- a/tests/integration/hosts/nuke/test_publish_in_nuke.py +++ b/tests/integration/hosts/nuke/test_publish_in_nuke.py @@ -1,6 +1,5 @@ import pytest import os -import shutil import logging from tests.lib.testing_classes import PublishTest diff --git a/tests/lib/testing_classes.py b/tests/lib/testing_classes.py index bf1c490ecdf..92b2b2b52b5 100644 --- a/tests/lib/testing_classes.py +++ b/tests/lib/testing_classes.py @@ -73,7 +73,7 @@ def download_test_data(self): str(tmpdir), file_name) - if ext.lstrip('.') in RemoteFileHandler.IMPLEMENTED_ZIP_FORMATS: #noqa E501 + if ext.lstrip('.') in RemoteFileHandler.IMPLEMENTED_ZIP_FORMATS: # noqa: E501 RemoteFileHandler.unzip(os.path.join(tmpdir, file_name)) print("Temporary folder created:: {}".format(tmpdir)) yield tmpdir From 89490f4b14815d402c12ab8680e69c3241760da5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20LORRAIN?= Date: Fri, 3 Dec 2021 17:22:27 +0100 Subject: [PATCH 081/492] add maya default render folder path to settings --- .../maya/plugins/publish/validate_render_image_rule.py | 8 +++++++- .../deadline/plugins/publish/submit_maya_deadline.py | 7 ++++++- openpype/settings/defaults/project_settings/maya.json | 3 ++- .../projects_schema/schemas/schema_maya_create.json | 5 +++++ 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_render_image_rule.py b/openpype/hosts/maya/plugins/publish/validate_render_image_rule.py index dad1691149b..e892b239ccd 100644 --- a/openpype/hosts/maya/plugins/publish/validate_render_image_rule.py +++ b/openpype/hosts/maya/plugins/publish/validate_render_image_rule.py @@ -23,7 +23,13 @@ class ValidateRenderImageRule(pyblish.api.InstancePlugin): def process(self, instance): - assert get_file_rule("images") == "renders", ( + default_render_file = instance.context.data.get('project_settings')\ + .get('maya') \ + .get('create') \ + .get('CreateRender') \ + .get('default_render_image_folder') + + assert get_file_rule("images") == default_render_file, ( "Workspace's `images` file rule must be set to: renders" ) diff --git a/openpype/modules/default_modules/deadline/plugins/publish/submit_maya_deadline.py b/openpype/modules/default_modules/deadline/plugins/publish/submit_maya_deadline.py index e6c42374ca3..51a19e2aadf 100644 --- a/openpype/modules/default_modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/openpype/modules/default_modules/deadline/plugins/publish/submit_maya_deadline.py @@ -394,9 +394,14 @@ def process(self, instance): self.log.debug(filepath) # Gather needed data ------------------------------------------------ + default_render_file = instance.context.data.get('project_settings')\ + .get('maya')\ + .get('create')\ + .get('CreateRender')\ + .get('default_render_image_folder') filename = os.path.basename(filepath) comment = context.data.get("comment", "") - dirname = os.path.join(workspace, "renders") + dirname = os.path.join(workspace, default_render_file) renderlayer = instance.data['setMembers'] # rs_beauty deadline_user = context.data.get("user", getpass.getuser()) jobname = "%s - %s" % (filename, instance.name) diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 73c75ef3ee3..cbcf8cd6007 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -43,7 +43,8 @@ "defaults": [ "Main" ], - "aov_separator": "underscore" + "aov_separator": "underscore", + "default_render_image_folder": "renders" }, "CreateAnimation": { "enabled": true, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json index e50357cc40c..088d5d1f963 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json @@ -58,6 +58,11 @@ {"underscore": "_ (underscore)"}, {"dot": ". (dot)"} ] + }, + { + "type": "text", + "key": "default_render_image_folder", + "label": "Default render image folder" } ] }, From efa7bffe066cac6afcb68616f7196dadfb4df53d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 Dec 2021 17:33:50 +0100 Subject: [PATCH 082/492] OP-2042 - added a bit of documentation --- tests/integration/README.md | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/tests/integration/README.md b/tests/integration/README.md index 81c07ec50cc..aeed53d0973 100644 --- a/tests/integration/README.md +++ b/tests/integration/README.md @@ -5,16 +5,23 @@ Contains end-to-end tests for automatic testing of OP. Should run headless publish on all hosts to check basic publish use cases automatically to limit regression issues. +How to run +---------- +- activate `{OPENPYPE_ROOT}/.venv` +- run in cmd +`{OPENPYPE_ROOT}/.venv/Scripts/python.exe {OPENPYPE_ROOT}/start.py runtests {OPENPYPE_ROOT}/tests/integration` + - add `hosts/APP_NAME` after integration part to limit only on specific app (eg. `{OPENPYPE_ROOT}/tests/integration/hosts/maya`) + How to create test for publishing from host ------------------------------------------ -- Extend PublishTest +- Extend PublishTest in `tests/lib/testing_classes.py` - Use `resources\test_data.zip` skeleton file as a template for testing input data - Put workfile into `test_data.zip/input/workfile` - If you require other than base DB dumps provide them to `test_data.zip/input/dumps` -- (Check commented code in `db_handler.py` how to dump specific DB. Currently all collections will be dumped.) - Implement `last_workfile_path` - `startup_scripts` - must contain pointing host to startup script saved into `test_data.zip/input/startup` - -- Script must contain something like + -- Script must contain something like (pseudocode) ``` import openpype from avalon import api, HOST @@ -25,13 +32,18 @@ pyblish.util.publish() EXIT_APP (command to exit host) ``` (Install and publish methods must be triggered only AFTER host app is fully initialized!) -- Zip `test_data.zip`, named it with descriptive name, upload it to Google Drive, right click - `Get link`, copy hash id +- If you would like add any command line arguments for your host app add it to `test_data.zip/input/app_args/app_args.json` (as a json list) +- Provide any required environment variables to `test_data.zip/input/env_vars/env_vars.json` (as a json dictionary) +- Zip `test_data.zip`, named it with descriptive name, upload it to Google Drive, right click - `Get link`, copy hash id (file must be accessible to anyone with a link!) - Put this hash id and zip file name into TEST_FILES [(HASH_ID, FILE_NAME, MD5_OPTIONAL)]. If you want to check MD5 of downloaded file, provide md5 value of zipped file. - Implement any assert checks you need in extended class - Run test class manually (via Pycharm or pytest runner (TODO)) -- If you want test to compare expected files to published one, set PERSIST to True, run test manually +- If you want test to visually compare expected files to published one, set PERSIST to True, run test manually -- Locate temporary `publish` subfolder of temporary folder (found in debugging console log) -- Copy whole folder content into .zip file into `expected` subfolder -- By default tests are comparing only structure of `expected` and published format (eg. if you want to save space, replace published files with empty files, but with expected names!) - -- Zip and upload again, change PERSIST to False \ No newline at end of file + -- Zip and upload again, change PERSIST to False + +- Use `TEST_DATA_FOLDER` variable in your class to reuse existing downloaded and unzipped test data (for faster creation of tests) +- Keep `APP_VARIANT` empty if you want to trigger test on latest version of app, or provide explicit value (as '2022' for Photoshop for example) \ No newline at end of file From 8eb7a30358275f47ca21691dca221fc00f2b4d57 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 3 Dec 2021 17:38:02 +0100 Subject: [PATCH 083/492] OP-2042 - added a functionality to inject additional command line arguments --- openpype/lib/applications.py | 2 ++ tests/lib/testing_classes.py | 30 +++++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/openpype/lib/applications.py b/openpype/lib/applications.py index 30be92e8869..6eb44a9694e 100644 --- a/openpype/lib/applications.py +++ b/openpype/lib/applications.py @@ -716,6 +716,8 @@ def __init__(self, application, executable, **data): # subprocess.Popen launch arguments (first argument in constructor) self.launch_args = executable.as_args() self.launch_args.extend(application.arguments) + if self.data.get("app_args"): + self.launch_args.extend(self.data.pop("app_args")) # Handle launch environemtns env = self.data.pop("env", None) diff --git a/tests/lib/testing_classes.py b/tests/lib/testing_classes.py index 92b2b2b52b5..15ab6857397 100644 --- a/tests/lib/testing_classes.py +++ b/tests/lib/testing_classes.py @@ -192,9 +192,35 @@ def last_workfile_path(self, download_test_data): def startup_scripts(self, monkeypatch_session, download_test_data): raise NotImplementedError + @pytest.fixture(scope="module") + def app_args(self, download_test_data): + """Returns additional application arguments from a test file. + + Test zip file should contain file at: + FOLDER_DIR/input/app_args/app_args.json + containing a list of command line arguments (like '-x' etc.) + """ + app_args = [] + args_url = os.path.join(download_test_data, "input", + "app_args", "app_args.json") + if not os.path.exists(args_url): + print("App argument file {} doesn't exist".format(args_url)) + else: + try: + with open(args_url) as json_file: + app_args = json.load(json_file) + + if not isinstance(app_args, list): + raise ValueError + except ValueError: + print("{} doesn't contain valid JSON".format(args_url)) + six.reraise(*sys.exc_info()) + + yield app_args + @pytest.fixture(scope="module") def launched_app(self, dbcon, download_test_data, last_workfile_path, - startup_scripts): + startup_scripts, app_args): """Launch host app""" # set publishing folders root_key = "config.roots.work.{}".format("windows") # TEMP @@ -228,6 +254,8 @@ def launched_app(self, dbcon, download_test_data, last_workfile_path, "asset_name": self.ASSET, "task_name": self.TASK } + if app_args: + data["app_args"] = app_args variant = self.APP_VARIANT if not variant: From 80d887f14e80a1c2862343f1407280c1341a44ec Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 3 Dec 2021 17:44:49 +0100 Subject: [PATCH 084/492] added 3 method related to openpype path --- igniter/bootstrap_repos.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/igniter/bootstrap_repos.py b/igniter/bootstrap_repos.py index ca64d193c76..921557b1a0d 100644 --- a/igniter/bootstrap_repos.py +++ b/igniter/bootstrap_repos.py @@ -346,6 +346,37 @@ def get_version_string_from_directory(repo_dir: Path) -> Union[str, None]: return version['__version__'] + @classmethod + def get_openpype_path(cls): + """Path to openpype zip directory. + + Path can be set through environment variable 'OPENPYPE_PATH' which + is set during start of OpenPype if is not available. + """ + return os.getenv("OPENPYPE_PATH") + + @classmethod + def openpype_path_is_set(cls): + """Path to OpenPype zip directory is set.""" + if cls.get_openpype_path(): + return True + return False + + @classmethod + def openpype_path_is_accessible(cls): + """Path to OpenPype zip directory is accessible. + + Exists for this machine. + """ + # First check if is set + if not cls.openpype_path_is_set(): + return False + + # Validate existence + if Path(cls.get_openpype_path()).exists(): + return True + return False + @staticmethod def get_available_versions( staging: bool = False, local: bool = False) -> List: From 494bd26d80e41e0169344442ae2b773642e4191f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 3 Dec 2021 18:04:18 +0100 Subject: [PATCH 085/492] created OpenPypeVersion wrapper in openpype lib --- openpype/lib/openpype_version.py | 59 ++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 openpype/lib/openpype_version.py diff --git a/openpype/lib/openpype_version.py b/openpype/lib/openpype_version.py new file mode 100644 index 00000000000..4d1a1a7bb82 --- /dev/null +++ b/openpype/lib/openpype_version.py @@ -0,0 +1,59 @@ +"""Lib access to OpenPypeVersion from igniter. + +Access to logic from igniter is available only for OpenPype processes. +Is meant to be able check OpenPype versions for studio. The logic is dependent +on igniter's logic of processing. +""" + +import sys + + +def get_OpenPypeVersion(): + """Access to OpenPypeVersion class stored in sys modules.""" + return sys.modules.get("OpenPypeVersion") + + +def op_version_control_available(): + """Check if current process has access to OpenPypeVersion.""" + if get_OpenPypeVersion() is None: + return False + return True + + +def get_available_versions(*args, **kwargs): + """Get list of available versions.""" + if op_version_control_available(): + return get_OpenPypeVersion().get_available_versions( + *args, **kwargs + ) + return None + + +def openpype_path_is_set(): + if op_version_control_available(): + return get_OpenPypeVersion().openpype_path_is_set() + return None + + +def openpype_path_is_accessible(): + if op_version_control_available(): + return get_OpenPypeVersion().openpype_path_is_accessible() + return None + + +def get_latest_version(): + if op_version_control_available(): + return get_OpenPypeVersion().get_latest_version() + return None + + +def get_production_version(): + if op_version_control_available(): + return get_OpenPypeVersion().get_production_version() + return None + + +def get_staging_version(): + if op_version_control_available(): + return get_OpenPypeVersion().get_staging_version() + return None From cc1cc49235f288ba95d8d213e5fe16a8c67d397f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 3 Dec 2021 18:06:52 +0100 Subject: [PATCH 086/492] added logic to load data for version entities --- openpype/settings/entities/enum_entity.py | 75 ++++++++++++++++++----- 1 file changed, 60 insertions(+), 15 deletions(-) diff --git a/openpype/settings/entities/enum_entity.py b/openpype/settings/entities/enum_entity.py index 5f0cbb12615..591b2dd94f8 100644 --- a/openpype/settings/entities/enum_entity.py +++ b/openpype/settings/entities/enum_entity.py @@ -1,8 +1,15 @@ import copy import sys +from openpype.lib.openpype_version import ( + op_version_control_available, + get_available_versions, + openpype_path_is_set, + openpype_path_is_accessible +) from .input_entities import InputEntity from .exceptions import EntitySchemaError from .lib import ( + OverrideState, NOT_SET, STRING_TYPE ) @@ -573,46 +580,84 @@ def _item_initialization(self): self.valid_value_types = (STRING_TYPE, ) self.value_on_not_set = "" - items = [ - {"": "Latest"} - ] - items.extend(self._get_openpype_versions()) + items = self._get_default_items() self.enum_items = items - self.valid_keys = { + self.valid_keys = self._extract_valid_keys(items) + + def _extract_valid_keys(self, items): + return { tuple(item.keys())[0] for item in items } + def _get_default_items(self): + return [ + {"": "Latest"} + ] + def _get_openpype_versions(self): return [] + def set_override_state(self, state, *args, **kwargs): + items = self._get_default_items() + versions = self._get_openpype_versions() + if versions is not None: + for version in versions: + items.append({version: version}) + + self.enum_items = items + self.valid_keys = self._extract_valid_keys(items) + + # Studio value is not available in collected versions + if ( + state is OverrideState.STUDIO + and self.had_studio_override + and self._studio_override_value not in self.valid_keys + ): + # Define if entity should keep the value in settings. + # Value is marked as not existing anymore if + # - openpype version control is available + # - path to openpype zips is set + # - path to openpype zips is accessible (existing for this machine) + keep_value = True + if ( + op_version_control_available() + and openpype_path_is_set() + and openpype_path_is_accessible() + ): + keep_value = False + + if keep_value: + self.enum_items.append( + {self._studio_override_value: self._studio_override_value} + ) + self.valid_keys.add(self._studio_override_value) + + super(_OpenPypeVersionEnum, self).set_override_state( + state, *args, **kwargs + ) + class ProductionVersionsEnumEntity(_OpenPypeVersionEnum): schema_types = ["production-versions-enum"] def _get_openpype_versions(self): - items = [] if "OpenPypeVersion" in sys.modules: OpenPypeVersion = sys.modules["OpenPypeVersion"] - versions = OpenPypeVersion.get_available_versions( + return get_available_versions( staging=False, local=False ) - for item in versions: - items.append({item: item}) - return items + return None class StagingVersionsEnumEntity(_OpenPypeVersionEnum): schema_types = ["staging-versions-enum"] def _get_openpype_versions(self): - items = [] if "OpenPypeVersion" in sys.modules: OpenPypeVersion = sys.modules["OpenPypeVersion"] - versions = OpenPypeVersion.get_available_versions( + return get_available_versions( staging=False, local=False ) - for item in versions: - items.append({item: item}) - return items + return None From e9b0347d8309705da17a57463f59a95bb8d1a042 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 3 Dec 2021 18:26:30 +0100 Subject: [PATCH 087/492] changed static method to class method --- igniter/bootstrap_repos.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/igniter/bootstrap_repos.py b/igniter/bootstrap_repos.py index 921557b1a0d..5cade8324ce 100644 --- a/igniter/bootstrap_repos.py +++ b/igniter/bootstrap_repos.py @@ -377,9 +377,9 @@ def openpype_path_is_accessible(cls): return True return False - @staticmethod + @classmethod def get_available_versions( - staging: bool = False, local: bool = False) -> List: + cls, staging: bool = False, local: bool = False) -> List: """Get ordered dict of detected OpenPype version. Resolution order for OpenPype is following: From 6762d6645957dc177796a067c64856fafe6096b2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 3 Dec 2021 18:26:54 +0100 Subject: [PATCH 088/492] added functions to retrieve local and remote versions separatelly --- igniter/bootstrap_repos.py | 84 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/igniter/bootstrap_repos.py b/igniter/bootstrap_repos.py index 5cade8324ce..c52175c8fda 100644 --- a/igniter/bootstrap_repos.py +++ b/igniter/bootstrap_repos.py @@ -377,6 +377,90 @@ def openpype_path_is_accessible(cls): return True return False + @classmethod + def get_local_versions( + cls, production: bool = None, staging: bool = None + ) -> List: + """Get all versions available on this machine. + + Arguments give ability to specify if filtering is needed. If both + arguments are set to None all found versions are returned. + + Args: + production (bool): Return production versions. + staging (bool): Return staging versions. + """ + # Return all local versions if arguments are set to None + if production is None and staging is None: + production = True + staging = True + + # Just return empty output if both are disabled + elif not production and not staging: + return [] + + dir_to_search = Path(user_data_dir("openpype", "pypeclub")) + versions = OpenPypeVersion.get_versions_from_directory( + dir_to_search + ) + filtered_versions = [] + for version in versions: + if version.is_staging(): + if staging: + filtered_versions.append(version) + elif production: + filtered_versions.append(version) + return list(sorted(set(filtered_versions))) + + @classmethod + def get_remote_versions( + cls, production: bool = None, staging: bool = None + ) -> List: + """Get all versions available in OpenPype Path. + + Arguments give ability to specify if filtering is needed. If both + arguments are set to None all found versions are returned. + + Args: + production (bool): Return production versions. + staging (bool): Return staging versions. + """ + # Return all local versions if arguments are set to None + if production is None and staging is None: + production = True + staging = True + + # Just return empty output if both are disabled + elif not production and not staging: + return [] + + dir_to_search = None + if cls.openpype_path_is_accessible(): + dir_to_search = Path(cls.get_openpype_path()) + else: + registry = OpenPypeSettingsRegistry() + try: + registry_dir = Path(str(registry.get_item("openPypePath"))) + if registry_dir.exists(): + dir_to_search = registry_dir + + except ValueError: + # nothing found in registry, we'll use data dir + pass + + if not dir_to_search: + return [] + + versions = cls.get_versions_from_directory(dir_to_search) + filtered_versions = [] + for version in versions: + if version.is_staging(): + if staging: + filtered_versions.append(version) + elif production: + filtered_versions.append(version) + return list(sorted(set(filtered_versions))) + @classmethod def get_available_versions( cls, staging: bool = False, local: bool = False) -> List: From 73f29819873c632111a3a2b3a644afc36f04509a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 3 Dec 2021 18:27:14 +0100 Subject: [PATCH 089/492] use new functions in get_available_versions --- igniter/bootstrap_repos.py | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/igniter/bootstrap_repos.py b/igniter/bootstrap_repos.py index c52175c8fda..5bb7296d3d3 100644 --- a/igniter/bootstrap_repos.py +++ b/igniter/bootstrap_repos.py @@ -479,29 +479,11 @@ def get_available_versions( local (bool, optional): List only local versions. """ - registry = OpenPypeSettingsRegistry() - dir_to_search = Path(user_data_dir("openpype", "pypeclub")) - user_versions = OpenPypeVersion.get_versions_from_directory( - dir_to_search) + user_versions = cls.get_local_versions() # if we have openpype_path specified, search only there. - + openpype_versions = [] if not local: - if os.getenv("OPENPYPE_PATH"): - if Path(os.getenv("OPENPYPE_PATH")).exists(): - dir_to_search = Path(os.getenv("OPENPYPE_PATH")) - else: - try: - registry_dir = Path( - str(registry.get_item("openPypePath"))) - if registry_dir.exists(): - dir_to_search = registry_dir - - except ValueError: - # nothing found in registry, we'll use data dir - pass - - openpype_versions = OpenPypeVersion.get_versions_from_directory( - dir_to_search) + openpype_versions = cls.get_remote_versions() openpype_versions += user_versions # remove duplicates and staging/production From a14662bccee58dd50faeb887842889ae7dc63ff1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 3 Dec 2021 18:35:21 +0100 Subject: [PATCH 090/492] fix args conditions --- igniter/bootstrap_repos.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/igniter/bootstrap_repos.py b/igniter/bootstrap_repos.py index 5bb7296d3d3..94f786e8692 100644 --- a/igniter/bootstrap_repos.py +++ b/igniter/bootstrap_repos.py @@ -395,8 +395,14 @@ def get_local_versions( production = True staging = True + elif production is None and not staging: + production = True + + elif staging is None and not production: + staging = True + # Just return empty output if both are disabled - elif not production and not staging: + if not production and not staging: return [] dir_to_search = Path(user_data_dir("openpype", "pypeclub")) @@ -430,8 +436,14 @@ def get_remote_versions( production = True staging = True + elif production is None and not staging: + production = True + + elif staging is None and not production: + staging = True + # Just return empty output if both are disabled - elif not production and not staging: + if not production and not staging: return [] dir_to_search = None From 1567afc4b9ba205669f3e716a96efb8963c5e79c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 3 Dec 2021 18:35:34 +0100 Subject: [PATCH 091/492] extended available functions --- openpype/lib/openpype_version.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/openpype/lib/openpype_version.py b/openpype/lib/openpype_version.py index 4d1a1a7bb82..42ee4543787 100644 --- a/openpype/lib/openpype_version.py +++ b/openpype/lib/openpype_version.py @@ -41,19 +41,31 @@ def openpype_path_is_accessible(): return None -def get_latest_version(): +def get_local_versions(*args, **kwargs): if op_version_control_available(): - return get_OpenPypeVersion().get_latest_version() + return get_OpenPypeVersion().get_local_versions(*args, **kwargs) return None -def get_production_version(): +def get_remote_versions(*args, **kwargs): + if op_version_control_available(): + return get_OpenPypeVersion().get_remote_versions(*args, **kwargs) + return None + + +def get_latest_version(*args, **kwargs): + if op_version_control_available(): + return get_OpenPypeVersion().get_latest_version(*args, **kwargs) + return None + + +def get_current_production_version(): if op_version_control_available(): return get_OpenPypeVersion().get_production_version() return None -def get_staging_version(): +def get_current_staging_version(): if op_version_control_available(): return get_OpenPypeVersion().get_staging_version() return None From 7aec21aa800dc225fbc5d2db9f46e8defcd26f29 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 3 Dec 2021 18:35:45 +0100 Subject: [PATCH 092/492] use extended functions in entities --- openpype/settings/entities/enum_entity.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/openpype/settings/entities/enum_entity.py b/openpype/settings/entities/enum_entity.py index 591b2dd94f8..534775a41de 100644 --- a/openpype/settings/entities/enum_entity.py +++ b/openpype/settings/entities/enum_entity.py @@ -2,7 +2,7 @@ import sys from openpype.lib.openpype_version import ( op_version_control_available, - get_available_versions, + get_remote_versions, openpype_path_is_set, openpype_path_is_accessible ) @@ -645,9 +645,7 @@ class ProductionVersionsEnumEntity(_OpenPypeVersionEnum): def _get_openpype_versions(self): if "OpenPypeVersion" in sys.modules: OpenPypeVersion = sys.modules["OpenPypeVersion"] - return get_available_versions( - staging=False, local=False - ) + return get_remote_versions(production=True) return None @@ -657,7 +655,5 @@ class StagingVersionsEnumEntity(_OpenPypeVersionEnum): def _get_openpype_versions(self): if "OpenPypeVersion" in sys.modules: OpenPypeVersion = sys.modules["OpenPypeVersion"] - return get_available_versions( - staging=False, local=False - ) + return get_remote_versions(staging=True) return None From b3ec2fe524dfd05ab60ef88649f40aa4b29ce3f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20LORRAIN?= Date: Mon, 6 Dec 2021 10:28:56 +0100 Subject: [PATCH 093/492] Use the setting in CollectRender --- openpype/hosts/maya/plugins/publish/collect_render.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/collect_render.py b/openpype/hosts/maya/plugins/publish/collect_render.py index 580d459a907..e26859ad934 100644 --- a/openpype/hosts/maya/plugins/publish/collect_render.py +++ b/openpype/hosts/maya/plugins/publish/collect_render.py @@ -221,14 +221,18 @@ def process(self, context): # append full path full_exp_files = [] aov_dict = {} - + default_render_file = context.data.get('project_settings')\ + .get('maya')\ + .get('create')\ + .get('CreateRender')\ + .get('default_render_image_folder') # replace relative paths with absolute. Render products are # returned as list of dictionaries. publish_meta_path = None for aov in exp_files: full_paths = [] for file in aov[aov.keys()[0]]: - full_path = os.path.join(workspace, "renders", file) + full_path = os.path.join(workspace, default_render_file, file) full_path = full_path.replace("\\", "/") full_paths.append(full_path) publish_meta_path = os.path.dirname(full_path) From ca1516fa13b03409c160f3c0f97785143d59fd4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20LORRAIN?= Date: Mon, 6 Dec 2021 10:45:22 +0100 Subject: [PATCH 094/492] Fix syntax --- openpype/hosts/maya/plugins/publish/collect_render.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/collect_render.py b/openpype/hosts/maya/plugins/publish/collect_render.py index e26859ad934..788ed12b414 100644 --- a/openpype/hosts/maya/plugins/publish/collect_render.py +++ b/openpype/hosts/maya/plugins/publish/collect_render.py @@ -222,17 +222,18 @@ def process(self, context): full_exp_files = [] aov_dict = {} default_render_file = context.data.get('project_settings')\ - .get('maya')\ - .get('create')\ - .get('CreateRender')\ - .get('default_render_image_folder') + .get('maya')\ + .get('create')\ + .get('CreateRender')\ + .get('default_render_image_folder') # replace relative paths with absolute. Render products are # returned as list of dictionaries. publish_meta_path = None for aov in exp_files: full_paths = [] for file in aov[aov.keys()[0]]: - full_path = os.path.join(workspace, default_render_file, file) + full_path = os.path.join(workspace, default_render_file, + file) full_path = full_path.replace("\\", "/") full_paths.append(full_path) publish_meta_path = os.path.dirname(full_path) From 8f0f0769800645e4966fc1e301ce3c1e97bde925 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 6 Dec 2021 11:51:35 +0100 Subject: [PATCH 095/492] added functions to get expected studio version --- igniter/bootstrap_repos.py | 23 ++++++++++++++++++++++- igniter/tools.py | 18 ++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/igniter/bootstrap_repos.py b/igniter/bootstrap_repos.py index 94f786e8692..9446b3e8ce2 100644 --- a/igniter/bootstrap_repos.py +++ b/igniter/bootstrap_repos.py @@ -22,7 +22,10 @@ OpenPypeSecureRegistry, OpenPypeSettingsRegistry ) -from .tools import get_openpype_path_from_db +from .tools import ( + get_openpype_path_from_db, + get_expected_studio_version_str +) LOG_INFO = 0 @@ -571,6 +574,24 @@ def get_latest_version( return openpype_versions[-1] + @classmethod + def get_expected_studio_version(cls, staging=False): + """Expected OpenPype version that should be used at the moment. + + If version is not defined in settings the latest found version is + used. + + Args: + staging (bool): Staging version or production version. + + Returns: + OpenPypeVersion: Version that should be used. + """ + result = get_expected_studio_version_str(staging) + if not result: + return cls.get_latest_version(staging, False) + return OpenPypeVersion(version=result) + class BootstrapRepos: """Class for bootstrapping local OpenPype installation. diff --git a/igniter/tools.py b/igniter/tools.py index 3e862f58030..5cad2b9bf80 100644 --- a/igniter/tools.py +++ b/igniter/tools.py @@ -182,6 +182,24 @@ def get_openpype_path_from_db(url: str) -> Union[str, None]: return None +def get_expected_studio_version_str(staging=False) -> str: + """Version that should be currently used in studio. + + Args: + staging (bool): Get current version for staging. + + Returns: + str: OpenPype version which should be used. Empty string means latest. + """ + mongo_url = os.environ.get("OPENPYPE_MONGO") + global_settings = get_openpype_global_settings(mongo_url) + if staging: + key = "staging_version" + else: + key = "production_version" + return global_settings.get(key) or "" + + def load_stylesheet() -> str: """Load css style sheet. From 7e04079151479548daf15ec81768c454935c71d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20LORRAIN?= Date: Mon, 6 Dec 2021 14:07:01 +0100 Subject: [PATCH 096/492] Update error message --- .../hosts/maya/plugins/publish/validate_render_image_rule.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_render_image_rule.py b/openpype/hosts/maya/plugins/publish/validate_render_image_rule.py index e892b239ccd..a912431b559 100644 --- a/openpype/hosts/maya/plugins/publish/validate_render_image_rule.py +++ b/openpype/hosts/maya/plugins/publish/validate_render_image_rule.py @@ -30,7 +30,9 @@ def process(self, instance): .get('default_render_image_folder') assert get_file_rule("images") == default_render_file, ( - "Workspace's `images` file rule must be set to: renders" + "Workspace's `images` file rule must be set to: {}".format( + default_render_file + ) ) @classmethod From 04c50532bb440f0d7d005c124495153f9e20076c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 6 Dec 2021 15:41:47 +0100 Subject: [PATCH 097/492] OP-2042 - added a functionality to run tests either with start.py or openpype_console --- openpype/pype_commands.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openpype/pype_commands.py b/openpype/pype_commands.py index ce1a9718b37..f01c6f0e42f 100644 --- a/openpype/pype_commands.py +++ b/openpype/pype_commands.py @@ -366,12 +366,12 @@ def run_tests(self, folder, mark, pyargs): pyargs_str = "--pyargs {}".format(pyargs) # disable warnings and show captured stdout even if success - args_str = "--disable-pytest-warnings -rP" + args = ["--disable-pytest-warnings", "-rP"] - cmd = "pytest {} {} {} {}".format(args_str, folder, - mark_str, pyargs_str) - print("Running {}".format(cmd)) - subprocess.run(cmd) + args += [folder, mark_str, pyargs_str] + print("run_tests args: {}".format(args)) + import pytest + pytest.main(args) def syncserver(self, active_site): """Start running sync_server in background.""" From 150f6efd08fcf5e1f83d25110c8b76c88b52856f Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 6 Dec 2021 17:00:28 +0100 Subject: [PATCH 098/492] OP-2042 - explicitly sort variants alphabetically --- openpype/lib/remote_publish.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/lib/remote_publish.py b/openpype/lib/remote_publish.py index d7db4d1ab9c..976de048f66 100644 --- a/openpype/lib/remote_publish.py +++ b/openpype/lib/remote_publish.py @@ -2,6 +2,7 @@ from datetime import datetime import sys from bson.objectid import ObjectId +import collections import pyblish.util import pyblish.api @@ -140,7 +141,9 @@ def find_variant_key(application_manager, host): found_variant_key = None # finds most up-to-date variant if any installed - for variant_key, variant in app_group.variants.items(): + sorted_variants = collections.OrderedDict( + sorted(app_group.variants.items())) + for variant_key, variant in sorted_variants.items(): for executable in variant.executables: if executable.exists(): found_variant_key = variant_key From eda5ff6b91b48bec5e747754cc9f664e7e47c3a8 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 6 Dec 2021 17:28:02 +0100 Subject: [PATCH 099/492] OP-2042 - better format of asserts --- .../hosts/nuke/test_publish_in_nuke.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/tests/integration/hosts/nuke/test_publish_in_nuke.py b/tests/integration/hosts/nuke/test_publish_in_nuke.py index fe1745299d9..d603b9f177a 100644 --- a/tests/integration/hosts/nuke/test_publish_in_nuke.py +++ b/tests/integration/hosts/nuke/test_publish_in_nuke.py @@ -24,7 +24,7 @@ class TestPublishInNuke(PublishTest): {OPENPYPE_ROOT}/.venv/Scripts/python.exe {OPENPYPE_ROOT}/start.py runtests ../tests/integration/hosts/nuke # noqa: E501 """ - PERSIST = False # True - keep test_db, test_openpype, outputted test files + PERSIST = True # True - keep test_db, test_openpype, outputted test files TEST_FILES = [ ("1SUurHj2aiQ21ZIMJfGVBI2KjR8kIjBGI", "test_Nuke_publish.zip", "") @@ -67,8 +67,10 @@ def startup_scripts(self, monkeypatch_session, download_test_data): def test_db_asserts(self, dbcon, publish_finished): """Host and input data dependent expected results in DB.""" print("test_db_asserts") - assert 5 == dbcon.count_documents({"type": "version"}), \ - "Not expected no of versions" + versions = dbcon.count_documents({"type": "version"}) + assert 5 == versions, \ + "Not expected no of versions. "\ + "Expected 5, found {}".format(versions) assert 0 == dbcon.count_documents({"type": "version", "name": {"$ne": 1}}), \ @@ -86,10 +88,12 @@ def test_db_asserts(self, dbcon, publish_finished): assert 10 == dbcon.count_documents({"type": "representation"}), \ "Not expected no of representations" - assert 1 == dbcon.count_documents({"type": "representation", + reprs = dbcon.count_documents({"type": "representation", "context.subset": "renderCompositingInNukeMain", # noqa: E501 - "context.ext": "exr"}), \ - "Not expected no of representations with ext 'exr'" + "context.ext": "exr"}) + assert 1 == reprs, \ + "Not expected no of representations with ext 'exr'."\ + "Expected 1, found {}".format(reprs) if __name__ == "__main__": From 4a93c6927d52b5f363a2c1ffb6130d84dc0613d5 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 6 Dec 2021 17:28:23 +0100 Subject: [PATCH 100/492] OP-2042 - added mention of possibility to trigger it via executables --- tests/integration/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/integration/README.md b/tests/integration/README.md index aeed53d0973..8839e2e43f6 100644 --- a/tests/integration/README.md +++ b/tests/integration/README.md @@ -11,6 +11,9 @@ How to run - run in cmd `{OPENPYPE_ROOT}/.venv/Scripts/python.exe {OPENPYPE_ROOT}/start.py runtests {OPENPYPE_ROOT}/tests/integration` - add `hosts/APP_NAME` after integration part to limit only on specific app (eg. `{OPENPYPE_ROOT}/tests/integration/hosts/maya`) + +OR can use built executables +`openpype_console runtests {ABS_PATH}/tests/integration` How to create test for publishing from host ------------------------------------------ From 12498862943b12ba7c039ba19b71d58fa7e85de5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 6 Dec 2021 18:10:29 +0100 Subject: [PATCH 101/492] text entity may have value hints --- openpype/settings/entities/input_entities.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/settings/entities/input_entities.py b/openpype/settings/entities/input_entities.py index a0598d405ed..d45e8f9f012 100644 --- a/openpype/settings/entities/input_entities.py +++ b/openpype/settings/entities/input_entities.py @@ -438,6 +438,7 @@ def _item_initialization(self): # GUI attributes self.multiline = self.schema_data.get("multiline", False) self.placeholder_text = self.schema_data.get("placeholder") + self.value_hints = self.schema_data.get("value_hints") or [] def _convert_to_valid_type(self, value): # Allow numbers converted to string From 376c4f977864a285b2ca2f271e1cf9f4e5571589 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 6 Dec 2021 18:10:49 +0100 Subject: [PATCH 102/492] changed openpype version enums to text inputs --- openpype/settings/entities/__init__.py | 14 +-- openpype/settings/entities/enum_entity.py | 87 ------------------- .../settings/entities/op_version_entity.py | 49 +++++++++++ .../schemas/system_schema/schema_general.json | 4 +- 4 files changed, 59 insertions(+), 95 deletions(-) create mode 100644 openpype/settings/entities/op_version_entity.py diff --git a/openpype/settings/entities/__init__.py b/openpype/settings/entities/__init__.py index e4a13b80536..4efd3582974 100644 --- a/openpype/settings/entities/__init__.py +++ b/openpype/settings/entities/__init__.py @@ -107,8 +107,6 @@ TaskTypeEnumEntity, DeadlineUrlEnumEntity, AnatomyTemplatesEnumEntity, - ProductionVersionsEnumEntity, - StagingVersionsEnumEntity ) from .list_entity import ListEntity @@ -124,7 +122,10 @@ ) from .anatomy_entities import AnatomyEntity - +from .op_version_entity import ( + ProductionVersionsInputEntity, + StagingVersionsInputEntity +) __all__ = ( "DefaultsNotDefined", @@ -171,8 +172,6 @@ "TaskTypeEnumEntity", "DeadlineUrlEnumEntity", "AnatomyTemplatesEnumEntity", - "ProductionVersionsEnumEntity", - "StagingVersionsEnumEntity", "ListEntity", @@ -185,5 +184,8 @@ "DictConditionalEntity", "SyncServerProviders", - "AnatomyEntity" + "AnatomyEntity", + + "ProductionVersionsInputEntity", + "StagingVersionsInputEntity" ) diff --git a/openpype/settings/entities/enum_entity.py b/openpype/settings/entities/enum_entity.py index 534775a41de..1f9b361f162 100644 --- a/openpype/settings/entities/enum_entity.py +++ b/openpype/settings/entities/enum_entity.py @@ -1,5 +1,4 @@ import copy -import sys from openpype.lib.openpype_version import ( op_version_control_available, get_remote_versions, @@ -9,7 +8,6 @@ from .input_entities import InputEntity from .exceptions import EntitySchemaError from .lib import ( - OverrideState, NOT_SET, STRING_TYPE ) @@ -572,88 +570,3 @@ def set_override_state(self, *args, **kwargs): self.enum_items, self.valid_keys = self._get_enum_values() if self._current_value not in self.valid_keys: self._current_value = self.value_on_not_set - - -class _OpenPypeVersionEnum(BaseEnumEntity): - def _item_initialization(self): - self.multiselection = False - self.valid_value_types = (STRING_TYPE, ) - - self.value_on_not_set = "" - items = self._get_default_items() - - self.enum_items = items - self.valid_keys = self._extract_valid_keys(items) - - def _extract_valid_keys(self, items): - return { - tuple(item.keys())[0] - for item in items - } - - def _get_default_items(self): - return [ - {"": "Latest"} - ] - - def _get_openpype_versions(self): - return [] - - def set_override_state(self, state, *args, **kwargs): - items = self._get_default_items() - versions = self._get_openpype_versions() - if versions is not None: - for version in versions: - items.append({version: version}) - - self.enum_items = items - self.valid_keys = self._extract_valid_keys(items) - - # Studio value is not available in collected versions - if ( - state is OverrideState.STUDIO - and self.had_studio_override - and self._studio_override_value not in self.valid_keys - ): - # Define if entity should keep the value in settings. - # Value is marked as not existing anymore if - # - openpype version control is available - # - path to openpype zips is set - # - path to openpype zips is accessible (existing for this machine) - keep_value = True - if ( - op_version_control_available() - and openpype_path_is_set() - and openpype_path_is_accessible() - ): - keep_value = False - - if keep_value: - self.enum_items.append( - {self._studio_override_value: self._studio_override_value} - ) - self.valid_keys.add(self._studio_override_value) - - super(_OpenPypeVersionEnum, self).set_override_state( - state, *args, **kwargs - ) - - -class ProductionVersionsEnumEntity(_OpenPypeVersionEnum): - schema_types = ["production-versions-enum"] - - def _get_openpype_versions(self): - if "OpenPypeVersion" in sys.modules: - OpenPypeVersion = sys.modules["OpenPypeVersion"] - return get_remote_versions(production=True) - return None - - -class StagingVersionsEnumEntity(_OpenPypeVersionEnum): - schema_types = ["staging-versions-enum"] - - def _get_openpype_versions(self): - if "OpenPypeVersion" in sys.modules: - OpenPypeVersion = sys.modules["OpenPypeVersion"] - return get_remote_versions(staging=True) - return None diff --git a/openpype/settings/entities/op_version_entity.py b/openpype/settings/entities/op_version_entity.py new file mode 100644 index 00000000000..e00c6a47371 --- /dev/null +++ b/openpype/settings/entities/op_version_entity.py @@ -0,0 +1,49 @@ +from openpype.lib.openpype_version import ( + op_version_control_available, + get_remote_versions, + openpype_path_is_set, + openpype_path_is_accessible +) +from .input_entities import TextEntity +from .lib import OverrideState + + +class _OpenPypeVersionInput(TextEntity): + def _item_initialization(self): + super(_OpenPypeVersionInput, self)._item_initialization() + self.multiline = False + self.placeholder_text = "Latest" + self.value_hints = [] + + def _get_openpype_versions(self): + return [] + + def set_override_state(self, state, *args, **kwargs): + value_hints = [] + if state is OverrideState.STUDIO: + versions = self._get_openpype_versions() + if versions is not None: + for version in versions: + value_hints.append(str(version)) + + self.value_hints = value_hints + + super(_OpenPypeVersionInput, self).set_override_state( + state, *args, **kwargs + ) + + +class ProductionVersionsInputEntity(_OpenPypeVersionInput): + schema_types = ["production-versions-text"] + + def _get_openpype_versions(self): + return ["", "asd", "dsa", "3.6"] + return get_remote_versions(production=True) + + +class StagingVersionsInputEntity(_OpenPypeVersionInput): + schema_types = ["staging-versions-text"] + + def _get_openpype_versions(self): + return ["", "asd+staging", "dsa+staging", "3.6+staging"] + return get_remote_versions(staging=True) diff --git a/openpype/settings/entities/schemas/system_schema/schema_general.json b/openpype/settings/entities/schemas/system_schema/schema_general.json index d548a22c975..b848a34dda3 100644 --- a/openpype/settings/entities/schemas/system_schema/schema_general.json +++ b/openpype/settings/entities/schemas/system_schema/schema_general.json @@ -31,12 +31,12 @@ "type": "splitter" }, { - "type": "production-versions-enum", + "type": "production-versions-text", "key": "production_version", "label": "Production version" }, { - "type": "staging-versions-enum", + "type": "staging-versions-text", "key": "staging_version", "label": "Staging version" }, From d856c4602b712d6619883dfb7da819eb2d01dd84 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 6 Dec 2021 18:11:26 +0100 Subject: [PATCH 103/492] added completer implementation for value hints --- openpype/tools/settings/settings/widgets.py | 220 ++++++++++++++++++++ 1 file changed, 220 insertions(+) diff --git a/openpype/tools/settings/settings/widgets.py b/openpype/tools/settings/settings/widgets.py index 7a7213fa66f..bf59f605c9c 100644 --- a/openpype/tools/settings/settings/widgets.py +++ b/openpype/tools/settings/settings/widgets.py @@ -1,4 +1,5 @@ import os +import copy from Qt import QtWidgets, QtCore, QtGui from avalon.vendor import qtawesome from avalon.mongodb import ( @@ -24,13 +25,232 @@ ) +class CompleterFilter(QtCore.QSortFilterProxyModel): + def __init__(self, *args, **kwargs): + super(CompleterFilter, self).__init__(*args, **kwargs) + + self.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive) + + self._text_filter = "" + + def set_text_filter(self, text): + if self._text_filter == text: + return + self._text_filter = text + self.invalidateFilter() + + def filterAcceptsRow(self, row, parent_index): + if not self._text_filter: + return True + model = self.sourceModel() + index = model.index(row, self.filterKeyColumn(), parent_index) + value = index.data(QtCore.Qt.DisplayRole) + if self._text_filter in value: + if self._text_filter == value: + return False + return True + return False + + +class CompleterView(QtWidgets.QListView): + row_activated = QtCore.Signal(str) + + def __init__(self, parent): + super(CompleterView, self).__init__(parent) + + self.setWindowFlags( + QtCore.Qt.FramelessWindowHint + | QtCore.Qt.Tool + ) + delegate = QtWidgets.QStyledItemDelegate() + self.setItemDelegate(delegate) + + model = QtGui.QStandardItemModel() + filter_model = CompleterFilter() + filter_model.setSourceModel(model) + self.setModel(filter_model) + + # self.installEventFilter(parent) + + self.clicked.connect(self._on_activated) + + self._last_loaded_values = None + self._model = model + self._filter_model = filter_model + self._delegate = delegate + + def _on_activated(self, index): + if index.isValid(): + value = index.data(QtCore.Qt.DisplayRole) + self.row_activated.emit(value) + + def set_text_filter(self, text): + self._filter_model.set_text_filter(text) + self._update_geo() + + def sizeHint(self): + result = super(CompleterView, self).sizeHint() + height = 0 + for row in range(self._filter_model.rowCount()): + height += self.sizeHintForRow(row) + result.setHeight(height) + return result + + def _update_geo(self): + size_hint = self.sizeHint() + self.resize(size_hint.width(), size_hint.height()) + + def update_values(self, values): + if not values: + values = [] + + if self._last_loaded_values == values: + return + self._last_loaded_values = copy.deepcopy(values) + + root_item = self._model.invisibleRootItem() + existing_values = {} + for row in reversed(range(root_item.rowCount())): + child = root_item.child(row) + value = child.data(QtCore.Qt.DisplayRole) + if value not in values: + root_item.removeRows(child.row()) + else: + existing_values[value] = child + + for row, value in enumerate(values): + if value in existing_values: + item = existing_values[value] + if item.row() == row: + continue + else: + item = QtGui.QStandardItem(value) + item.setEditable(False) + + root_item.setChild(row, item) + + self._update_geo() + + def _get_selected_row(self): + indexes = self.selectionModel().selectedIndexes() + if not indexes: + return -1 + return indexes[0].row() + + def _select_row(self, row): + index = self._filter_model.index(row, 0) + self.setCurrentIndex(index) + + def move_up(self): + rows = self._filter_model.rowCount() + if rows == 0: + return + + selected_row = self._get_selected_row() + if selected_row < 0: + new_row = rows - 1 + else: + new_row = selected_row - 1 + if new_row < 0: + new_row = rows - 1 + + if new_row != selected_row: + self._select_row(new_row) + + def move_down(self): + rows = self._filter_model.rowCount() + if rows == 0: + return + + selected_row = self._get_selected_row() + if selected_row < 0: + new_row = 0 + else: + new_row = selected_row + 1 + if new_row >= rows: + new_row = 0 + + if new_row != selected_row: + self._select_row(new_row) + + def enter_pressed(self): + selected_row = self._get_selected_row() + if selected_row < 0: + return + index = self._filter_model.index(selected_row, 0) + self._on_activated(index) + + class SettingsLineEdit(QtWidgets.QLineEdit): focused_in = QtCore.Signal() + def __init__(self, *args, **kwargs): + super(SettingsLineEdit, self).__init__(*args, **kwargs) + + self._completer = None + + self.textChanged.connect(self._on_text_change) + + def _on_text_change(self, text): + if self._completer is not None: + self._completer.set_text_filter(text) + + def _update_completer(self): + if self._completer is None or not self._completer.isVisible(): + return + point = self.frameGeometry().bottomLeft() + new_point = self.mapToGlobal(point) + self._completer.move(new_point) + def focusInEvent(self, event): super(SettingsLineEdit, self).focusInEvent(event) self.focused_in.emit() + if self._completer is None: + return + self._completer.show() + self._update_completer() + + def focusOutEvent(self, event): + super(SettingsLineEdit, self).focusOutEvent(event) + if self._completer is not None: + self._completer.hide() + + def paintEvent(self, event): + super(SettingsLineEdit, self).paintEvent(event) + self._update_completer() + + def update_completer_values(self, values): + if not values and self._completer is None: + return + + self._create_completer() + + self._completer.update_values(values) + + def _create_completer(self): + if self._completer is None: + self._completer = CompleterView(self) + self._completer.row_activated.connect(self._completer_activated) + + def _completer_activated(self, text): + self.setText(text) + + def keyPressEvent(self, event): + if self._completer is None: + super(SettingsLineEdit, self).keyPressEvent(event) + return + + key = event.key() + if key == QtCore.Qt.Key_Up: + self._completer.move_up() + elif key == QtCore.Qt.Key_Down: + self._completer.move_down() + elif key in (QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return): + self._completer.enter_pressed() + else: + super(SettingsLineEdit, self).keyPressEvent(event) + class SettingsPlainTextEdit(QtWidgets.QPlainTextEdit): focused_in = QtCore.Signal() From 4c2ccb014c3c2a1c815638b5e9f90c86fd36c5cb Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 6 Dec 2021 18:12:17 +0100 Subject: [PATCH 104/492] text input fills value hints --- openpype/tools/settings/settings/item_widgets.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/openpype/tools/settings/settings/item_widgets.py b/openpype/tools/settings/settings/item_widgets.py index 2e00967a604..f20deeb6e46 100644 --- a/openpype/tools/settings/settings/item_widgets.py +++ b/openpype/tools/settings/settings/item_widgets.py @@ -2,6 +2,9 @@ from Qt import QtWidgets, QtCore, QtGui +from openpype.widgets.sliders import NiceSlider +from openpype.tools.settings import CHILD_OFFSET + from .widgets import ( ExpandingWidget, NumberSpinBox, @@ -22,9 +25,6 @@ InputWidget ) -from openpype.widgets.sliders import NiceSlider -from openpype.tools.settings import CHILD_OFFSET - class DictImmutableKeysWidget(BaseWidget): def create_ui(self): @@ -378,6 +378,11 @@ def _add_inputs_to_layout(self): self.input_field.focused_in.connect(self._on_input_focus) self.input_field.textChanged.connect(self._on_value_change) + self._refresh_completer() + + def _refresh_completer(self): + self.input_field.update_completer_values(self.entity.value_hints) + def _on_input_focus(self): self.focused_in() From d7ad673462547ef8c653e2041e71b04594a31d3f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 6 Dec 2021 18:15:26 +0100 Subject: [PATCH 105/492] removed test data --- openpype/settings/entities/op_version_entity.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openpype/settings/entities/op_version_entity.py b/openpype/settings/entities/op_version_entity.py index e00c6a47371..a6954119aa8 100644 --- a/openpype/settings/entities/op_version_entity.py +++ b/openpype/settings/entities/op_version_entity.py @@ -37,7 +37,6 @@ class ProductionVersionsInputEntity(_OpenPypeVersionInput): schema_types = ["production-versions-text"] def _get_openpype_versions(self): - return ["", "asd", "dsa", "3.6"] return get_remote_versions(production=True) @@ -45,5 +44,4 @@ class StagingVersionsInputEntity(_OpenPypeVersionInput): schema_types = ["staging-versions-text"] def _get_openpype_versions(self): - return ["", "asd+staging", "dsa+staging", "3.6+staging"] return get_remote_versions(staging=True) From fc9a50f7423ecf7e684396313a1679e2f3d07b44 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 6 Dec 2021 18:15:54 +0100 Subject: [PATCH 106/492] added special widget for openpype version input --- openpype/settings/entities/op_version_entity.py | 10 +++++----- openpype/tools/settings/settings/categories.py | 7 +++++++ openpype/tools/settings/settings/item_widgets.py | 7 +++++++ 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/openpype/settings/entities/op_version_entity.py b/openpype/settings/entities/op_version_entity.py index a6954119aa8..2458f038527 100644 --- a/openpype/settings/entities/op_version_entity.py +++ b/openpype/settings/entities/op_version_entity.py @@ -8,9 +8,9 @@ from .lib import OverrideState -class _OpenPypeVersionInput(TextEntity): +class OpenPypeVersionInput(TextEntity): def _item_initialization(self): - super(_OpenPypeVersionInput, self)._item_initialization() + super(OpenPypeVersionInput, self)._item_initialization() self.multiline = False self.placeholder_text = "Latest" self.value_hints = [] @@ -28,19 +28,19 @@ def set_override_state(self, state, *args, **kwargs): self.value_hints = value_hints - super(_OpenPypeVersionInput, self).set_override_state( + super(OpenPypeVersionInput, self).set_override_state( state, *args, **kwargs ) -class ProductionVersionsInputEntity(_OpenPypeVersionInput): +class ProductionVersionsInputEntity(OpenPypeVersionInput): schema_types = ["production-versions-text"] def _get_openpype_versions(self): return get_remote_versions(production=True) -class StagingVersionsInputEntity(_OpenPypeVersionInput): +class StagingVersionsInputEntity(OpenPypeVersionInput): schema_types = ["staging-versions-text"] def _get_openpype_versions(self): diff --git a/openpype/tools/settings/settings/categories.py b/openpype/tools/settings/settings/categories.py index a6e4154b2b7..af7e0bd742a 100644 --- a/openpype/tools/settings/settings/categories.py +++ b/openpype/tools/settings/settings/categories.py @@ -28,6 +28,9 @@ StudioDefaultsNotDefined, SchemaError ) +from openpype.settings.entities.op_version_entity import ( + OpenPypeVersionInput +) from openpype.settings import SaveWarningExc from .widgets import ProjectListWidget @@ -46,6 +49,7 @@ BoolWidget, DictImmutableKeysWidget, TextWidget, + OpenPypeVersionText, NumberWidget, RawJsonWidget, EnumeratorWidget, @@ -116,6 +120,9 @@ def create_ui_for_entity(category_widget, entity, entity_widget): elif isinstance(entity, BoolEntity): return BoolWidget(*args) + elif isinstance(entity, OpenPypeVersionInput): + return OpenPypeVersionText(*args) + elif isinstance(entity, TextEntity): return TextWidget(*args) diff --git a/openpype/tools/settings/settings/item_widgets.py b/openpype/tools/settings/settings/item_widgets.py index f20deeb6e46..11f0dc6add2 100644 --- a/openpype/tools/settings/settings/item_widgets.py +++ b/openpype/tools/settings/settings/item_widgets.py @@ -389,6 +389,7 @@ def _on_input_focus(self): def _on_entity_change(self): if self.entity.value != self.input_value(): self.set_entity_value() + self._refresh_completer() def set_entity_value(self): if self.entity.multiline: @@ -411,6 +412,12 @@ def _on_value_change_timer(self): self.entity.set(self.input_value()) +class OpenPypeVersionText(TextWidget): + def _on_entity_change(self): + super(OpenPypeVersionText, self)._on_entity_change() + self._refresh_completer() + + class NumberWidget(InputWidget): _slider_widget = None From 8b4455721ed26a5bae3a4d74efe88d03a1854a45 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 6 Dec 2021 18:22:01 +0100 Subject: [PATCH 107/492] removed overdone refresh of completer --- openpype/tools/settings/settings/item_widgets.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/tools/settings/settings/item_widgets.py b/openpype/tools/settings/settings/item_widgets.py index 11f0dc6add2..bca70edff5c 100644 --- a/openpype/tools/settings/settings/item_widgets.py +++ b/openpype/tools/settings/settings/item_widgets.py @@ -389,7 +389,6 @@ def _on_input_focus(self): def _on_entity_change(self): if self.entity.value != self.input_value(): self.set_entity_value() - self._refresh_completer() def set_entity_value(self): if self.entity.multiline: From af12e59f826f3b27c3b6c8256650da1360fcffb2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 6 Dec 2021 18:23:40 +0100 Subject: [PATCH 108/492] skip completer refresh for multiline entities --- openpype/tools/settings/settings/item_widgets.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/openpype/tools/settings/settings/item_widgets.py b/openpype/tools/settings/settings/item_widgets.py index bca70edff5c..3a7e1268462 100644 --- a/openpype/tools/settings/settings/item_widgets.py +++ b/openpype/tools/settings/settings/item_widgets.py @@ -378,9 +378,12 @@ def _add_inputs_to_layout(self): self.input_field.focused_in.connect(self._on_input_focus) self.input_field.textChanged.connect(self._on_value_change) - self._refresh_completer() - def _refresh_completer(self): + # Multiline entity can't have completer + # - there is not space for this UI component + if self.entity.multiline: + return + self.input_field.update_completer_values(self.entity.value_hints) def _on_input_focus(self): From 8e853314daac94ce4ecf9966ff85d72c75347a03 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 6 Dec 2021 18:23:53 +0100 Subject: [PATCH 109/492] added schema validation for value hints --- openpype/settings/entities/input_entities.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/openpype/settings/entities/input_entities.py b/openpype/settings/entities/input_entities.py index d45e8f9f012..a285bf34336 100644 --- a/openpype/settings/entities/input_entities.py +++ b/openpype/settings/entities/input_entities.py @@ -440,6 +440,15 @@ def _item_initialization(self): self.placeholder_text = self.schema_data.get("placeholder") self.value_hints = self.schema_data.get("value_hints") or [] + def schema_validations(self): + if self.multiline and self.value_hints: + reason = ( + "TextEntity entity can't use value hints" + " for multiline input (yet)." + ) + raise EntitySchemaError(self, reason) + super(TextEntity, self).schema_validations() + def _convert_to_valid_type(self, value): # Allow numbers converted to string if isinstance(value, (int, float)): From 298d2da581805e3f704c651bda6ed7e680d58bf4 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 6 Dec 2021 18:57:36 +0100 Subject: [PATCH 110/492] added refresh of completer to init --- openpype/tools/settings/settings/item_widgets.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/tools/settings/settings/item_widgets.py b/openpype/tools/settings/settings/item_widgets.py index 3a7e1268462..1d912a90b78 100644 --- a/openpype/tools/settings/settings/item_widgets.py +++ b/openpype/tools/settings/settings/item_widgets.py @@ -378,6 +378,8 @@ def _add_inputs_to_layout(self): self.input_field.focused_in.connect(self._on_input_focus) self.input_field.textChanged.connect(self._on_value_change) + self._refresh_completer() + def _refresh_completer(self): # Multiline entity can't have completer # - there is not space for this UI component From c479fdecdb13085ed4d58c5c044ccf77587c3e76 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 7 Dec 2021 10:36:13 +0100 Subject: [PATCH 111/492] added label --- .../entities/schemas/system_schema/schema_general.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openpype/settings/entities/schemas/system_schema/schema_general.json b/openpype/settings/entities/schemas/system_schema/schema_general.json index b848a34dda3..ed66cd7ac8a 100644 --- a/openpype/settings/entities/schemas/system_schema/schema_general.json +++ b/openpype/settings/entities/schemas/system_schema/schema_general.json @@ -30,6 +30,10 @@ { "type": "splitter" }, + { + "type": "label", + "label": "Define explicit OpenPype version that should be used. Keep empty to use latest available version." + }, { "type": "production-versions-text", "key": "production_version", From c0ad1b5d8b635d24ccb81ac69bc9e38544d6eec9 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 7 Dec 2021 11:01:55 +0100 Subject: [PATCH 112/492] OP-2042 - use existing value of env var OPENPYPE_DATABASE_NAME if possible For better automatic testing --- start.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/start.py b/start.py index 0f7e82071d7..ae6aefe34ee 100644 --- a/start.py +++ b/start.py @@ -925,7 +925,9 @@ def boot(): sys.exit(1) os.environ["OPENPYPE_MONGO"] = openpype_mongo - os.environ["OPENPYPE_DATABASE_NAME"] = "openpype" # name of Pype database + # name of Pype database + os.environ["OPENPYPE_DATABASE_NAME"] = \ + os.environ.get("OPENPYPE_DATABASE_NAME") or "openpype" _print(">>> run disk mapping command ...") run_disk_mapping_commands(openpype_mongo) From 709d0ee625dfad38b70590ee1804b0f376ce4a10 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 7 Dec 2021 11:02:16 +0100 Subject: [PATCH 113/492] OP-2042 - reset connection to openpype DB --- tests/lib/testing_classes.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/lib/testing_classes.py b/tests/lib/testing_classes.py index 15ab6857397..0a04dc59c2e 100644 --- a/tests/lib/testing_classes.py +++ b/tests/lib/testing_classes.py @@ -108,6 +108,14 @@ def env_var(self, monkeypatch_session, download_test_data): import openpype openpype_root = os.path.dirname(os.path.dirname(openpype.__file__)) + + #reset connection to openpype DB with new env var + import openpype.settings.lib as sett_lib + sett_lib._SETTINGS_HANDLER = None + sett_lib._LOCAL_SETTINGS_HANDLER = None + sett_lib.create_settings_handler() + sett_lib.create_local_settings_handler() + # ?? why 2 of those monkeypatch_session.setenv("OPENPYPE_ROOT", openpype_root) monkeypatch_session.setenv("OPENPYPE_REPOS_ROOT", openpype_root) From 1ea180ba8d58fdb4010eefad0739e84d3f1482e9 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 7 Dec 2021 11:21:48 +0100 Subject: [PATCH 114/492] removed dot --- .../settings/entities/schemas/system_schema/schema_general.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/settings/entities/schemas/system_schema/schema_general.json b/openpype/settings/entities/schemas/system_schema/schema_general.json index ed66cd7ac8a..b4c83fc85f0 100644 --- a/openpype/settings/entities/schemas/system_schema/schema_general.json +++ b/openpype/settings/entities/schemas/system_schema/schema_general.json @@ -20,7 +20,7 @@ }, { "type": "label", - "label": "This is NOT a securely stored password!. It only acts as a simple barrier to stop users from accessing studio wide settings." + "label": "This is NOT a securely stored password! It only acts as a simple barrier to stop users from accessing studio wide settings." }, { "type": "text", From 1454efab3a0528ccaa8cbcb417773fafd8f10e2d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 7 Dec 2021 13:43:51 +0100 Subject: [PATCH 115/492] added style for completer view --- openpype/style/style.css | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/openpype/style/style.css b/openpype/style/style.css index 19245cdc402..0ef15a25113 100644 --- a/openpype/style/style.css +++ b/openpype/style/style.css @@ -536,6 +536,27 @@ QAbstractItemView::branch:!has-children:!has-siblings:adjoins-item { background: transparent; } +CompleterView { + border: 1px solid #555555; + background: {color:bg-inputs}; +} + +CompleterView::item:selected { + background: {color:bg-view-hover}; +} + +CompleterView::item:selected:hover { + background: {color:bg-view-hover}; +} + +CompleterView::right-arrow { + min-width: 10px; +} +CompleterView::separator { + background: {color:bg-menu-separator}; + height: 2px; + margin-right: 5px; +} /* Progress bar */ QProgressBar { From 44e00ed1d442d3d9dc3cb0a68a8976fb63da401c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 7 Dec 2021 13:44:02 +0100 Subject: [PATCH 116/492] don't care about ideal height --- openpype/tools/settings/settings/widgets.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/openpype/tools/settings/settings/widgets.py b/openpype/tools/settings/settings/widgets.py index bf59f605c9c..4b88c1f93ff 100644 --- a/openpype/tools/settings/settings/widgets.py +++ b/openpype/tools/settings/settings/widgets.py @@ -90,10 +90,9 @@ def set_text_filter(self, text): def sizeHint(self): result = super(CompleterView, self).sizeHint() - height = 0 - for row in range(self._filter_model.rowCount()): - height += self.sizeHintForRow(row) - result.setHeight(height) + if self._filter_model.rowCount() == 0: + result.setHeight(0) + return result def _update_geo(self): From 228d6fc914f3f1e6bcd6212d323facb9b67d4364 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 7 Dec 2021 13:45:21 +0100 Subject: [PATCH 117/492] added basic information about valid version --- .../tools/settings/settings/item_widgets.py | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/openpype/tools/settings/settings/item_widgets.py b/openpype/tools/settings/settings/item_widgets.py index 1d912a90b78..0c661623752 100644 --- a/openpype/tools/settings/settings/item_widgets.py +++ b/openpype/tools/settings/settings/item_widgets.py @@ -417,9 +417,30 @@ def _on_value_change_timer(self): class OpenPypeVersionText(TextWidget): + def __init__(self, *args, **kwargs): + self._info_widget = None + super(OpenPypeVersionText, self).__init__(*args, **kwargs) + + def create_ui(self): + super(OpenPypeVersionText, self).create_ui() + info_widget = QtWidgets.QLabel("Latest", self) + self.content_layout.addWidget(info_widget, 1) + + self._info_widget = info_widget + + def _update_info_widget(self): + value = self.input_value() + if value == "": + self._info_widget.setText("Latest") + elif value in self.entity.value_hints: + self._info_widget.setText("Ok") + else: + self._info_widget.setText("Version not found from this workstation") + def _on_entity_change(self): super(OpenPypeVersionText, self)._on_entity_change() self._refresh_completer() + self._update_info_widget() class NumberWidget(InputWidget): From 87b606582a95696f4a4fc3091c4cbd624538d0b7 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 7 Dec 2021 18:10:13 +0100 Subject: [PATCH 118/492] do not create new qapplication if already exists --- igniter/__init__.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/igniter/__init__.py b/igniter/__init__.py index bbc3dbfc88c..d974bd9e0d3 100644 --- a/igniter/__init__.py +++ b/igniter/__init__.py @@ -28,7 +28,9 @@ def open_dialog(): if scale_attr is not None: QtWidgets.QApplication.setAttribute(scale_attr) - app = QtWidgets.QApplication(sys.argv) + app = QtWidgets.QApplication.instance() + if not app: + app = QtWidgets.QApplication(sys.argv) d = InstallDialog() d.open() @@ -49,7 +51,9 @@ def open_update_window(openpype_version): if scale_attr is not None: QtWidgets.QApplication.setAttribute(scale_attr) - app = QtWidgets.QApplication(sys.argv) + app = QtWidgets.QApplication.instance() + if not app: + app = QtWidgets.QApplication(sys.argv) d = UpdateWindow(version=openpype_version) d.open() From db467dcbfd041a7fdd60399a2535bf215e40a7c3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 7 Dec 2021 18:13:56 +0100 Subject: [PATCH 119/492] added function to get openpype icon to tools.py --- igniter/install_dialog.py | 5 +++-- igniter/tools.py | 8 ++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/igniter/install_dialog.py b/igniter/install_dialog.py index 1fe67e33975..251adebc9ff 100644 --- a/igniter/install_dialog.py +++ b/igniter/install_dialog.py @@ -12,7 +12,8 @@ from .install_thread import InstallThread from .tools import ( validate_mongo_connection, - get_openpype_path_from_db + get_openpype_path_from_db, + get_openpype_icon_path ) from .nice_progress_bar import NiceProgressBar @@ -187,7 +188,6 @@ def __init__(self, parent=None): current_dir = os.path.dirname(os.path.abspath(__file__)) roboto_font_path = os.path.join(current_dir, "RobotoMono-Regular.ttf") poppins_font_path = os.path.join(current_dir, "Poppins") - icon_path = os.path.join(current_dir, "openpype_icon.png") # Install roboto font QtGui.QFontDatabase.addApplicationFont(roboto_font_path) @@ -196,6 +196,7 @@ def __init__(self, parent=None): QtGui.QFontDatabase.addApplicationFont(filename) # Load logo + icon_path = get_openpype_icon_path() pixmap_openpype_logo = QtGui.QPixmap(icon_path) # Set logo as icon of window self.setWindowIcon(QtGui.QIcon(pixmap_openpype_logo)) diff --git a/igniter/tools.py b/igniter/tools.py index 5cad2b9bf80..72b98f1f824 100644 --- a/igniter/tools.py +++ b/igniter/tools.py @@ -210,3 +210,11 @@ def load_stylesheet() -> str: stylesheet_path = Path(__file__).parent.resolve() / "stylesheet.css" return stylesheet_path.read_text() + + +def get_openpype_icon_path() -> str: + """Path to OpenPype icon png file.""" + return os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "openpype_icon.png" + ) From e4534c5074244afa096bc290809011d0b24eb7a6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 7 Dec 2021 18:21:05 +0100 Subject: [PATCH 120/492] added new message dialog to show a dialog --- igniter/__init__.py | 22 ++++++++++++++++++++ igniter/message_dialog.py | 44 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 igniter/message_dialog.py diff --git a/igniter/__init__.py b/igniter/__init__.py index d974bd9e0d3..6f06c1eb90e 100644 --- a/igniter/__init__.py +++ b/igniter/__init__.py @@ -63,9 +63,31 @@ def open_update_window(openpype_version): return version_path +def show_message_dialog(title, message): + if os.getenv("OPENPYPE_HEADLESS_MODE"): + print("!!! Can't open dialog in headless mode. Exiting.") + sys.exit(1) + from Qt import QtWidgets, QtCore + from .message_dialog import MessageDialog + + scale_attr = getattr(QtCore.Qt, "AA_EnableHighDpiScaling", None) + if scale_attr is not None: + QtWidgets.QApplication.setAttribute(scale_attr) + + app = QtWidgets.QApplication.instance() + if not app: + app = QtWidgets.QApplication(sys.argv) + + dialog = MessageDialog(title, message) + dialog.open() + + app.exec_() + + __all__ = [ "BootstrapRepos", "open_dialog", "open_update_window", + "show_message_dialog", "version" ] diff --git a/igniter/message_dialog.py b/igniter/message_dialog.py new file mode 100644 index 00000000000..88a086df1e8 --- /dev/null +++ b/igniter/message_dialog.py @@ -0,0 +1,44 @@ +from Qt import QtWidgets, QtGui + +from .tools import ( + load_stylesheet, + get_openpype_icon_path +) + + +class MessageDialog(QtWidgets.QDialog): + def __init__(self, title, message): + super(MessageDialog, self).__init__() + + # Set logo as icon of window + icon_path = get_openpype_icon_path() + pixmap_openpype_logo = QtGui.QPixmap(icon_path) + self.setWindowIcon(QtGui.QIcon(pixmap_openpype_logo)) + + # Set title + self.setWindowTitle(title) + + # Set message + label_widget = QtWidgets.QLabel(message, self) + + ok_btn = QtWidgets.QPushButton("OK", self) + btns_layout = QtWidgets.QHBoxLayout() + btns_layout.addStretch(1) + btns_layout.addWidget(ok_btn, 0) + + layout = QtWidgets.QVBoxLayout(self) + # layout.setContentsMargins(0, 0, 0, 0) + layout.addWidget(label_widget, 1) + layout.addLayout(btns_layout, 0) + + ok_btn.clicked.connect(self._on_ok_clicked) + + self._label_widget = label_widget + self._ok_btn = ok_btn + + def _on_ok_clicked(self): + self.close() + + def showEvent(self, event): + super(MessageDialog, self).showEvent(event) + self.setStyleSheet(load_stylesheet()) From 500339088d51a2c66536aaffd5bd07ea7a4f6619 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 7 Dec 2021 18:24:25 +0100 Subject: [PATCH 121/492] added new exception into tools --- igniter/tools.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/igniter/tools.py b/igniter/tools.py index 72b98f1f824..2595140582a 100644 --- a/igniter/tools.py +++ b/igniter/tools.py @@ -16,6 +16,11 @@ ) +class OpenPypeVersionNotFound(Exception): + """OpenPype version was not found in remote and local repository.""" + pass + + def should_add_certificate_path_to_mongo_url(mongo_url): """Check if should add ca certificate to mongo url. From 21b9926be40079b04d970c690d11db8ffb421329 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 7 Dec 2021 18:30:15 +0100 Subject: [PATCH 122/492] simplified _find_frozen_openpype --- start.py | 120 +++++++++++++++++++++++-------------------------------- 1 file changed, 49 insertions(+), 71 deletions(-) diff --git a/start.py b/start.py index 05b7da6308e..91221da75da 100644 --- a/start.py +++ b/start.py @@ -196,7 +196,8 @@ def _print(message: str): from igniter.tools import ( get_openpype_global_settings, get_openpype_path_from_db, - validate_mongo_connection + validate_mongo_connection, + OpenPypeVersionNotFound ) # noqa from igniter.bootstrap_repos import OpenPypeVersion # noqa: E402 @@ -645,67 +646,60 @@ def _find_frozen_openpype(use_version: str = None, (if requested). """ - version_path = None - openpype_version = None - openpype_versions = bootstrap.find_openpype(include_zips=True, - staging=use_staging) - # get local frozen version and add it to detected version so if it is - # newer it will be used instead. - local_version_str = bootstrap.get_version( - Path(os.environ["OPENPYPE_ROOT"])) + # Collect OpenPype versions + openpype_versions = bootstrap.find_openpype( + include_zips=True, + staging=use_staging + ) + openpype_root = Path(os.environ["OPENPYPE_ROOT"]) + local_version_str = bootstrap.get_version(openpype_root) + studio_version = OpenPypeVersion.get_expected_studio_version(use_staging) if local_version_str: local_version = OpenPypeVersion( version=local_version_str, - path=Path(os.environ["OPENPYPE_ROOT"])) + path=openpype_root + ) if local_version not in openpype_versions: openpype_versions.append(local_version) - openpype_versions.sort() - # if latest is currently running, ditch whole list - # and run from current without installing it. - if local_version == openpype_versions[-1]: - os.environ["OPENPYPE_TRYOUT"] = "1" - openpype_versions = [] - else: - _print("!!! Warning: cannot determine current running version.") - if not os.getenv("OPENPYPE_TRYOUT"): - try: - # use latest one found (last in the list is latest) - openpype_version = openpype_versions[-1] - except IndexError: - # no OpenPype version found, run Igniter and ask for them. - _print('*** No OpenPype versions found.') - if os.getenv("OPENPYPE_HEADLESS_MODE") == "1": - _print("!!! Cannot open Igniter dialog in headless mode.") - sys.exit(1) - _print("--- launching setup UI ...") - import igniter - return_code = igniter.open_dialog() - if return_code == 2: - os.environ["OPENPYPE_TRYOUT"] = "1" - if return_code == 3: - # run OpenPype after installation - - _print('>>> Finding OpenPype again ...') - openpype_versions = bootstrap.find_openpype( - staging=use_staging) - try: - openpype_version = openpype_versions[-1] - except IndexError: - _print(("!!! Something is wrong and we didn't " - "found it again.")) - sys.exit(1) - elif return_code != 2: - _print(f" . finished ({return_code})") - sys.exit(return_code) + # Find OpenPype version that should be used + openpype_version = None + if use_version is not None: + # Version was specified with arguments or env OPENPYPE_VERSION + # - should crash if version is not available + _print("Finding specified version \"{}\"".format(use_version)) + for version in openpype_versions: + if version == use_version: + openpype_version = version + break - if not openpype_versions: - # no openpype versions found anyway, lets use then the one - # shipped with frozen OpenPype - if not os.getenv("OPENPYPE_TRYOUT"): - _print("*** Still no luck finding OpenPype.") - _print(("*** We'll try to use the one coming " - "with OpenPype installation.")) + if openpype_version is None: + raise OpenPypeVersionNotFound( + "Requested version \"{}\" was not found.".format(use_version) + ) + + elif studio_version: + _print("Finding studio version \"{}\"".format(studio_version)) + # Look for version specified by settings + for version in openpype_versions: + if version == studio_version: + openpype_version = version + break + if openpype_version is None: + raise OpenPypeVersionNotFound(( + "Requested OpenPype version \"{}\" defined by settings" + " was not found." + ).format(use_version)) + + else: + # Use latest available version + _print("Finding latest version") + openpype_versions.sort() + openpype_version = openpype_versions[-1] + + # get local frozen version and add it to detected version so if it is + # newer it will be used instead. + if local_version == openpype_version: version_path = _bootstrap_from_code(use_version, use_staging) openpype_version = OpenPypeVersion( version=BootstrapRepos.get_version(version_path), @@ -713,22 +707,6 @@ def _find_frozen_openpype(use_version: str = None, _initialize_environment(openpype_version) return version_path - # get path of version specified in `--use-version` - local_version = bootstrap.get_version(OPENPYPE_ROOT) - if use_version and use_version != local_version: - # force the one user has selected - openpype_version = None - openpype_versions = bootstrap.find_openpype(include_zips=True, - staging=use_staging) - v: OpenPypeVersion - found = [v for v in openpype_versions if str(v) == use_version] - if found: - openpype_version = sorted(found)[-1] - if not openpype_version: - _print(f"!!! Requested version {use_version} was not found.") - list_versions(openpype_versions, local_version) - sys.exit(1) - # test if latest detected is installed (in user data dir) is_inside = False try: From 5d03abe627d890785e8657931559f8dac39d66b1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 7 Dec 2021 18:30:51 +0100 Subject: [PATCH 123/492] removed play animation part --- start.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/start.py b/start.py index 91221da75da..5f416a47b81 100644 --- a/start.py +++ b/start.py @@ -866,16 +866,6 @@ def boot(): # ------------------------------------------------------------------------ _startup_validations() - # ------------------------------------------------------------------------ - # Play animation - # ------------------------------------------------------------------------ - - # from igniter.terminal_splash import play_animation - - # don't play for silenced commands - # if all(item not in sys.argv for item in silent_commands): - # play_animation() - # ------------------------------------------------------------------------ # Process arguments # ------------------------------------------------------------------------ From 9b369de50c2ad5a281fb0ff84fcb7988f26c224f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 7 Dec 2021 18:38:43 +0100 Subject: [PATCH 124/492] raise OpenPypeVersionNotFound in run from code too --- start.py | 59 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/start.py b/start.py index 5f416a47b81..69774c3a6ca 100644 --- a/start.py +++ b/start.py @@ -769,45 +769,50 @@ def _bootstrap_from_code(use_version, use_staging): # get current version of OpenPype local_version = bootstrap.get_local_live_version() - version_to_use = None openpype_versions = bootstrap.find_openpype( include_zips=True, staging=use_staging) if use_staging and not use_version: - try: - version_to_use = openpype_versions[-1] - except IndexError: - _print("!!! No staging versions are found.") - list_versions(openpype_versions, local_version) - sys.exit(1) + if not openpype_versions: + raise OpenPypeVersionNotFound( + "Didn't find any staging versions." + ) + + # Use last found staging version + version_to_use = openpype_versions[-1] if version_to_use.path.is_file(): version_to_use.path = bootstrap.extract_openpype( version_to_use) bootstrap.add_paths_from_directory(version_to_use.path) os.environ["OPENPYPE_VERSION"] = str(version_to_use) version_path = version_to_use.path - os.environ["OPENPYPE_REPOS_ROOT"] = (version_path / "openpype").as_posix() # noqa: E501 + os.environ["OPENPYPE_REPOS_ROOT"] = ( + version_path / "openpype" + ).as_posix() _openpype_root = version_to_use.path.as_posix() elif use_version and use_version != local_version: - v: OpenPypeVersion - found = [v for v in openpype_versions if str(v) == use_version] - if found: - version_to_use = sorted(found)[-1] - - if version_to_use: - # use specified - if version_to_use.path.is_file(): - version_to_use.path = bootstrap.extract_openpype( - version_to_use) - bootstrap.add_paths_from_directory(version_to_use.path) - os.environ["OPENPYPE_VERSION"] = use_version - version_path = version_to_use.path - os.environ["OPENPYPE_REPOS_ROOT"] = (version_path / "openpype").as_posix() # noqa: E501 - _openpype_root = version_to_use.path.as_posix() - else: - _print(f"!!! Requested version {use_version} was not found.") - list_versions(openpype_versions, local_version) - sys.exit(1) + version_to_use = None + for version in openpype_versions: + if str(version) == use_version: + version_to_use = version + break + + if version_to_use is None: + raise OpenPypeVersionNotFound( + "Requested version \"{}\" was not found.".format(use_version) + ) + + # use specified + if version_to_use.path.is_file(): + version_to_use.path = bootstrap.extract_openpype( + version_to_use) + bootstrap.add_paths_from_directory(version_to_use.path) + os.environ["OPENPYPE_VERSION"] = use_version + version_path = version_to_use.path + os.environ["OPENPYPE_REPOS_ROOT"] = ( + version_path / "openpype" + ).as_posix() + _openpype_root = version_to_use.path.as_posix() else: os.environ["OPENPYPE_VERSION"] = local_version version_path = Path(_openpype_root) From a98bdc7d7f2e91ce6c3855d6ca0ed95c6e84be45 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 7 Dec 2021 18:39:24 +0100 Subject: [PATCH 125/492] catch and handle OpenPypeVersioNotFound exception --- start.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/start.py b/start.py index 69774c3a6ca..853e6d13a08 100644 --- a/start.py +++ b/start.py @@ -972,6 +972,15 @@ def boot(): # find versions of OpenPype to be used with frozen code try: version_path = _find_frozen_openpype(use_version, use_staging) + except OpenPypeVersionNotFound as exc: + message = str(exc) + _print(message) + if os.environ.get("OPENPYPE_HEADLESS_MODE") == "1": + list_versions(openpype_versions, local_version) + else: + igniter.show_message_dialog("Version not found", message) + sys.exit(1) + except RuntimeError as e: # no version to run _print(f"!!! {e}") @@ -984,7 +993,17 @@ def boot(): sys.exit(1) _print(f"--- version is valid") else: - version_path = _bootstrap_from_code(use_version, use_staging) + try: + version_path = _bootstrap_from_code(use_version, use_staging) + + except OpenPypeVersionNotFound as exc: + message = str(exc) + _print(message) + if os.environ.get("OPENPYPE_HEADLESS_MODE") == "1": + list_versions(openpype_versions, local_version) + else: + igniter.show_message_dialog("Version not found", message) + sys.exit(1) # set this to point either to `python` from venv in case of live code # or to `openpype` or `openpype_console` in case of frozen code From b0099ea5d4271d997f80932535cc2817574fc73a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 7 Dec 2021 18:39:45 +0100 Subject: [PATCH 126/492] skipped already done imports --- start.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/start.py b/start.py index 853e6d13a08..32fa4bd062c 100644 --- a/start.py +++ b/start.py @@ -521,7 +521,7 @@ def _process_arguments() -> tuple: if os.getenv("OPENPYPE_HEADLESS_MODE") == "1": _print("!!! Cannot open Igniter dialog in headless mode.") sys.exit(1) - import igniter + return_code = igniter.open_dialog() # this is when we want to run OpenPype without installing anything. @@ -719,12 +719,12 @@ def _find_frozen_openpype(use_version: str = None, if not is_inside: # install latest version to user data dir - if os.getenv("OPENPYPE_HEADLESS_MODE", "0") != "1": - import igniter - version_path = igniter.open_update_window(openpype_version) - else: + if os.getenv("OPENPYPE_HEADLESS_MODE") == "1": version_path = bootstrap.install_version( - openpype_version, force=True) + openpype_version, force=True + ) + else: + version_path = igniter.open_update_window(openpype_version) openpype_version.path = version_path _initialize_environment(openpype_version) From 23ed14a3973316324e9cac2bdddd2d65ea31ab41 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 8 Dec 2021 11:41:02 +0100 Subject: [PATCH 127/492] OP-2042 - remove superfluous logging --- openpype/lib/path_tools.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/lib/path_tools.py b/openpype/lib/path_tools.py index 6fd0ad0dfe2..9bb0231ca74 100644 --- a/openpype/lib/path_tools.py +++ b/openpype/lib/path_tools.py @@ -307,7 +307,6 @@ def _get_local_sync_dirmap(self, project_settings): mapping = {} if not project_settings["global"]["sync_server"]["enabled"]: - log.debug("Site Sync not enabled") return mapping from openpype.settings.lib import get_site_local_overrides From 04689e72c0d1e1a546f737aff18b3c52c221ae90 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 8 Dec 2021 12:42:17 +0100 Subject: [PATCH 128/492] flame: moving `utility_scripts` to api folder also with `scripts` @iLLiCiTiT suggestion --- openpype/hosts/flame/{ => api}/scripts/wiretap_com.py | 0 .../export_preset/openpype_seg_thumbnails_jpg.xml | 0 .../export_preset/openpype_seg_video_h264.xml | 0 .../openpype_flame_to_ftrack/modules/__init__.py | 0 .../openpype_flame_to_ftrack/modules/app_utils.py | 0 .../openpype_flame_to_ftrack/modules/ftrack_lib.py | 0 .../openpype_flame_to_ftrack/modules/panel_app.py | 0 .../openpype_flame_to_ftrack/modules/uiwidgets.py | 0 .../openpype_flame_to_ftrack/openpype_flame_to_ftrack.py | 0 .../hosts/flame/{ => api}/utility_scripts/openpype_in_flame.py | 0 openpype/hosts/flame/api/utils.py | 1 + openpype/hosts/flame/hooks/pre_flame_setup.py | 2 +- 12 files changed, 2 insertions(+), 1 deletion(-) rename openpype/hosts/flame/{ => api}/scripts/wiretap_com.py (100%) rename openpype/hosts/flame/{ => api}/utility_scripts/openpype_flame_to_ftrack/export_preset/openpype_seg_thumbnails_jpg.xml (100%) rename openpype/hosts/flame/{ => api}/utility_scripts/openpype_flame_to_ftrack/export_preset/openpype_seg_video_h264.xml (100%) rename openpype/hosts/flame/{ => api}/utility_scripts/openpype_flame_to_ftrack/modules/__init__.py (100%) rename openpype/hosts/flame/{ => api}/utility_scripts/openpype_flame_to_ftrack/modules/app_utils.py (100%) rename openpype/hosts/flame/{ => api}/utility_scripts/openpype_flame_to_ftrack/modules/ftrack_lib.py (100%) rename openpype/hosts/flame/{ => api}/utility_scripts/openpype_flame_to_ftrack/modules/panel_app.py (100%) rename openpype/hosts/flame/{ => api}/utility_scripts/openpype_flame_to_ftrack/modules/uiwidgets.py (100%) rename openpype/hosts/flame/{ => api}/utility_scripts/openpype_flame_to_ftrack/openpype_flame_to_ftrack.py (100%) rename openpype/hosts/flame/{ => api}/utility_scripts/openpype_in_flame.py (100%) diff --git a/openpype/hosts/flame/scripts/wiretap_com.py b/openpype/hosts/flame/api/scripts/wiretap_com.py similarity index 100% rename from openpype/hosts/flame/scripts/wiretap_com.py rename to openpype/hosts/flame/api/scripts/wiretap_com.py diff --git a/openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/export_preset/openpype_seg_thumbnails_jpg.xml b/openpype/hosts/flame/api/utility_scripts/openpype_flame_to_ftrack/export_preset/openpype_seg_thumbnails_jpg.xml similarity index 100% rename from openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/export_preset/openpype_seg_thumbnails_jpg.xml rename to openpype/hosts/flame/api/utility_scripts/openpype_flame_to_ftrack/export_preset/openpype_seg_thumbnails_jpg.xml diff --git a/openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/export_preset/openpype_seg_video_h264.xml b/openpype/hosts/flame/api/utility_scripts/openpype_flame_to_ftrack/export_preset/openpype_seg_video_h264.xml similarity index 100% rename from openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/export_preset/openpype_seg_video_h264.xml rename to openpype/hosts/flame/api/utility_scripts/openpype_flame_to_ftrack/export_preset/openpype_seg_video_h264.xml diff --git a/openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/modules/__init__.py b/openpype/hosts/flame/api/utility_scripts/openpype_flame_to_ftrack/modules/__init__.py similarity index 100% rename from openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/modules/__init__.py rename to openpype/hosts/flame/api/utility_scripts/openpype_flame_to_ftrack/modules/__init__.py diff --git a/openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/modules/app_utils.py b/openpype/hosts/flame/api/utility_scripts/openpype_flame_to_ftrack/modules/app_utils.py similarity index 100% rename from openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/modules/app_utils.py rename to openpype/hosts/flame/api/utility_scripts/openpype_flame_to_ftrack/modules/app_utils.py diff --git a/openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/modules/ftrack_lib.py b/openpype/hosts/flame/api/utility_scripts/openpype_flame_to_ftrack/modules/ftrack_lib.py similarity index 100% rename from openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/modules/ftrack_lib.py rename to openpype/hosts/flame/api/utility_scripts/openpype_flame_to_ftrack/modules/ftrack_lib.py diff --git a/openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/modules/panel_app.py b/openpype/hosts/flame/api/utility_scripts/openpype_flame_to_ftrack/modules/panel_app.py similarity index 100% rename from openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/modules/panel_app.py rename to openpype/hosts/flame/api/utility_scripts/openpype_flame_to_ftrack/modules/panel_app.py diff --git a/openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/modules/uiwidgets.py b/openpype/hosts/flame/api/utility_scripts/openpype_flame_to_ftrack/modules/uiwidgets.py similarity index 100% rename from openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/modules/uiwidgets.py rename to openpype/hosts/flame/api/utility_scripts/openpype_flame_to_ftrack/modules/uiwidgets.py diff --git a/openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/openpype_flame_to_ftrack.py b/openpype/hosts/flame/api/utility_scripts/openpype_flame_to_ftrack/openpype_flame_to_ftrack.py similarity index 100% rename from openpype/hosts/flame/utility_scripts/openpype_flame_to_ftrack/openpype_flame_to_ftrack.py rename to openpype/hosts/flame/api/utility_scripts/openpype_flame_to_ftrack/openpype_flame_to_ftrack.py diff --git a/openpype/hosts/flame/utility_scripts/openpype_in_flame.py b/openpype/hosts/flame/api/utility_scripts/openpype_in_flame.py similarity index 100% rename from openpype/hosts/flame/utility_scripts/openpype_in_flame.py rename to openpype/hosts/flame/api/utility_scripts/openpype_in_flame.py diff --git a/openpype/hosts/flame/api/utils.py b/openpype/hosts/flame/api/utils.py index a750046362a..201c7d2facb 100644 --- a/openpype/hosts/flame/api/utils.py +++ b/openpype/hosts/flame/api/utils.py @@ -27,6 +27,7 @@ def _sync_utility_scripts(env=None): fsd_paths = [os.path.join( HOST_DIR, + "api", "utility_scripts" )] diff --git a/openpype/hosts/flame/hooks/pre_flame_setup.py b/openpype/hosts/flame/hooks/pre_flame_setup.py index 718c4b574c9..159fb374102 100644 --- a/openpype/hosts/flame/hooks/pre_flame_setup.py +++ b/openpype/hosts/flame/hooks/pre_flame_setup.py @@ -22,7 +22,7 @@ class FlamePrelaunch(PreLaunchHook): flame_python_exe = "/opt/Autodesk/python/2021/bin/python2.7" wtc_script_path = os.path.join( - opflame.HOST_DIR, "scripts", "wiretap_com.py") + opflame.HOST_DIR, "api", "scripts", "wiretap_com.py") def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) From b3d2d41267c313a822c186af9b0232f29baad85e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 8 Dec 2021 12:58:37 +0100 Subject: [PATCH 129/492] added working state context to category widget --- openpype/tools/settings/settings/categories.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/openpype/tools/settings/settings/categories.py b/openpype/tools/settings/settings/categories.py index a6e4154b2b7..79b5de952bf 100644 --- a/openpype/tools/settings/settings/categories.py +++ b/openpype/tools/settings/settings/categories.py @@ -1,6 +1,7 @@ import os import sys import traceback +import contextlib from enum import Enum from Qt import QtWidgets, QtCore, QtGui @@ -309,6 +310,12 @@ def add_widget_to_layout(self, widget, label_widget=None): ) self.content_layout.addWidget(widget, 0) + @contextlib.contextmanager + def working_state_context(self): + self.set_state(CategoryState.Working) + yield + self.set_state(CategoryState.Idle) + def save(self): if not self.items_are_valid(): return From 347b8da8a7a9a1a6c25d113fa6a2588b55acac04 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 8 Dec 2021 12:59:01 +0100 Subject: [PATCH 130/492] use working_state_context for current actions --- openpype/tools/settings/settings/base.py | 39 ++++++++++++++++-------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/openpype/tools/settings/settings/base.py b/openpype/tools/settings/settings/base.py index f8378ed18cb..1df7b2c75aa 100644 --- a/openpype/tools/settings/settings/base.py +++ b/openpype/tools/settings/settings/base.py @@ -110,9 +110,10 @@ def _discard_changes_action(self, menu, actions_mapping): return def discard_changes(): - self.ignore_input_changes.set_ignore(True) - self.entity.discard_changes() - self.ignore_input_changes.set_ignore(False) + with self.category_widget.working_state_context(): + self.ignore_input_changes.set_ignore(True) + self.entity.discard_changes() + self.ignore_input_changes.set_ignore(False) action = QtWidgets.QAction("Discard changes") actions_mapping[action] = discard_changes @@ -124,8 +125,11 @@ def _add_to_studio_default(self, menu, actions_mapping): if not self.entity.can_trigger_add_to_studio_default: return + def add_to_studio_default(): + with self.category_widget.working_state_context(): + self.entity.add_to_studio_default() action = QtWidgets.QAction("Add to studio default") - actions_mapping[action] = self.entity.add_to_studio_default + actions_mapping[action] = add_to_studio_default menu.addAction(action) def _remove_from_studio_default_action(self, menu, actions_mapping): @@ -133,9 +137,10 @@ def _remove_from_studio_default_action(self, menu, actions_mapping): return def remove_from_studio_default(): - self.ignore_input_changes.set_ignore(True) - self.entity.remove_from_studio_default() - self.ignore_input_changes.set_ignore(False) + with self.category_widget.working_state_context(): + self.ignore_input_changes.set_ignore(True) + self.entity.remove_from_studio_default() + self.ignore_input_changes.set_ignore(False) action = QtWidgets.QAction("Remove from studio default") actions_mapping[action] = remove_from_studio_default menu.addAction(action) @@ -144,8 +149,12 @@ def _add_to_project_override_action(self, menu, actions_mapping): if not self.entity.can_trigger_add_to_project_override: return + def add_to_project_override(): + with self.category_widget.working_state_context(): + self.entity.add_to_project_override + action = QtWidgets.QAction("Add to project project override") - actions_mapping[action] = self.entity.add_to_project_override + actions_mapping[action] = add_to_project_override menu.addAction(action) def _remove_from_project_override_action(self, menu, actions_mapping): @@ -153,9 +162,11 @@ def _remove_from_project_override_action(self, menu, actions_mapping): return def remove_from_project_override(): - self.ignore_input_changes.set_ignore(True) - self.entity.remove_from_project_override() - self.ignore_input_changes.set_ignore(False) + with self.category_widget.working_state_context(): + self.ignore_input_changes.set_ignore(True) + self.entity.remove_from_project_override() + self.ignore_input_changes.set_ignore(False) + action = QtWidgets.QAction("Remove from project override") actions_mapping[action] = remove_from_project_override menu.addAction(action) @@ -257,14 +268,16 @@ def _set_entity_value(_entity, _value): # Simple paste value method def paste_value(): - _set_entity_value(self.entity, value) + with self.category_widget.working_state_context(): + _set_entity_value(self.entity, value) action = QtWidgets.QAction("Paste", menu) output.append((action, paste_value)) # Paste value to matchin entity def paste_value_to_path(): - _set_entity_value(matching_entity, value) + with self.category_widget.working_state_context(): + _set_entity_value(matching_entity, value) if matching_entity is not None: action = QtWidgets.QAction("Paste to same place", menu) From 74b6da96aca81a54e32f8984d865d6f5674c4061 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 8 Dec 2021 12:59:21 +0100 Subject: [PATCH 131/492] added method to return project names from project list --- openpype/tools/settings/settings/widgets.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/openpype/tools/settings/settings/widgets.py b/openpype/tools/settings/settings/widgets.py index ac9870287b6..4c7bf87ce89 100644 --- a/openpype/tools/settings/settings/widgets.py +++ b/openpype/tools/settings/settings/widgets.py @@ -747,6 +747,13 @@ def select_project(self, project_name): index, QtCore.QItemSelectionModel.SelectionFlag.SelectCurrent ) + def get_project_names(self): + output = [] + for row in range(self.project_proxy.rowCount()): + index = self.project_proxy.index(row, 0) + output.append(index.data(PROJECT_NAME_ROLE)) + return output + def refresh(self): selected_project = None for index in self.project_list.selectedIndexes(): From e2f24fb87b3b732740649cf7f70fb32784530dcd Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 8 Dec 2021 12:59:42 +0100 Subject: [PATCH 132/492] project category widget has ability to retrieve project names --- openpype/tools/settings/settings/categories.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/openpype/tools/settings/settings/categories.py b/openpype/tools/settings/settings/categories.py index 79b5de952bf..029619849ed 100644 --- a/openpype/tools/settings/settings/categories.py +++ b/openpype/tools/settings/settings/categories.py @@ -606,6 +606,14 @@ def ui_tweaks(self): self.project_list_widget = project_list_widget + def get_project_names(self): + if ( + self.modify_defaults_checkbox + and self.modify_defaults_checkbox.isChecked() + ): + return [] + return self.project_list_widget.get_project_names() + def on_saved(self, saved_tab_widget): """Callback on any tab widget save. From 2fdac8639a00e58cb5544a6cdf9822d2bfa3d2c9 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 8 Dec 2021 13:00:07 +0100 Subject: [PATCH 133/492] initial version of applying settings from different project --- openpype/tools/settings/settings/base.py | 43 ++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/openpype/tools/settings/settings/base.py b/openpype/tools/settings/settings/base.py index 1df7b2c75aa..f03d0c61865 100644 --- a/openpype/tools/settings/settings/base.py +++ b/openpype/tools/settings/settings/base.py @@ -1,7 +1,10 @@ import json from Qt import QtWidgets, QtGui, QtCore + +from openpype.settings.entities import ProjectSettings from openpype.tools.settings import CHILD_OFFSET + from .widgets import ExpandingWidget from .lib import create_deffered_value_change_timer @@ -285,6 +288,45 @@ def paste_value_to_path(): return output + def _apply_values_from_project_action(self, menu, actions_mapping): + for attr_name in ("project_name", "get_project_names"): + if not hasattr(self.category_widget, attr_name): + return + + if self.entity.is_dynamic_item or self.entity.is_in_dynamic_item: + return + + submenu = QtWidgets.QMenu("Apply values from", menu) + + current_project_name = self.category_widget.project_name + for project_name in self.category_widget.get_project_names(): + if current_project_name == project_name: + continue + + if project_name is None: + project_name = "< Default >" + + action = QtWidgets.QAction(project_name) + submenu.addAction(action) + actions_mapping[action] = lambda: self._apply_values_from_project( + project_name + ) + menu.addMenu(submenu) + + def _apply_values_from_project(self, project_name): + with self.category_widget.working_state_context(): + path_keys = [ + item + for item in self.entity.path.split("/") + if item + ] + + settings = ProjectSettings(project_name) + entity = settings + for key in path_keys: + entity = entity[key] + self.entity.set(entity.value) + def show_actions_menu(self, event=None): if event and event.button() != QtCore.Qt.RightButton: return @@ -303,6 +345,7 @@ def show_actions_menu(self, event=None): self._remove_from_studio_default_action(menu, actions_mapping) self._add_to_project_override_action(menu, actions_mapping) self._remove_from_project_override_action(menu, actions_mapping) + self._apply_values_from_project_action(menu, actions_mapping) ui_actions = [] ui_actions.extend(self._copy_value_actions(menu)) From c08b4673fd5fd935f41b5277c8977e80aaeb595b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 8 Dec 2021 13:07:38 +0100 Subject: [PATCH 134/492] use defined constant for default project label --- openpype/tools/settings/settings/base.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/tools/settings/settings/base.py b/openpype/tools/settings/settings/base.py index f03d0c61865..94687f4f350 100644 --- a/openpype/tools/settings/settings/base.py +++ b/openpype/tools/settings/settings/base.py @@ -7,6 +7,7 @@ from .widgets import ExpandingWidget from .lib import create_deffered_value_change_timer +from .constants import DEFAULT_PROJECT_LABEL class BaseWidget(QtWidgets.QWidget): @@ -304,7 +305,7 @@ def _apply_values_from_project_action(self, menu, actions_mapping): continue if project_name is None: - project_name = "< Default >" + project_name = DEFAULT_PROJECT_LABEL action = QtWidgets.QAction(project_name) submenu.addAction(action) From 8270f2fc402a7cb119fdc71ead0bd474f0d7c9d6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 8 Dec 2021 13:07:57 +0100 Subject: [PATCH 135/492] catch exceptions happened during applying values from different project --- openpype/tools/settings/settings/base.py | 42 +++++++++++++++++------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/openpype/tools/settings/settings/base.py b/openpype/tools/settings/settings/base.py index 94687f4f350..6776845125b 100644 --- a/openpype/tools/settings/settings/base.py +++ b/openpype/tools/settings/settings/base.py @@ -1,4 +1,6 @@ +import sys import json +import traceback from Qt import QtWidgets, QtGui, QtCore @@ -316,17 +318,35 @@ def _apply_values_from_project_action(self, menu, actions_mapping): def _apply_values_from_project(self, project_name): with self.category_widget.working_state_context(): - path_keys = [ - item - for item in self.entity.path.split("/") - if item - ] - - settings = ProjectSettings(project_name) - entity = settings - for key in path_keys: - entity = entity[key] - self.entity.set(entity.value) + try: + path_keys = [ + item + for item in self.entity.path.split("/") + if item + ] + entity = ProjectSettings(project_name) + for key in path_keys: + entity = entity[key] + self.entity.set(entity.value) + + except Exception: + if project_name is None: + project_name = DEFAULT_PROJECT_LABEL + + # TODO better message + title = "Applying values failed" + msg = "Using values from project \"{}\" failed.".format( + project_name + ) + detail_msg = "".join( + traceback.format_exception(*sys.exc_info()) + ) + dialog = QtWidgets.QMessageBox(self) + dialog.setWindowTitle(title) + dialog.setIcon(QtWidgets.QMessageBox.Warning) + dialog.setText(msg) + dialog.setDetailedText(detail_msg) + dialog.exec_() def show_actions_menu(self, event=None): if event and event.button() != QtCore.Qt.RightButton: From 8e71919145a1ad491511697486238e8ebc82221e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 8 Dec 2021 13:25:22 +0100 Subject: [PATCH 136/492] skip action if does not have any projects which can be used as source for values --- openpype/tools/settings/settings/base.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/openpype/tools/settings/settings/base.py b/openpype/tools/settings/settings/base.py index 6776845125b..9016e639706 100644 --- a/openpype/tools/settings/settings/base.py +++ b/openpype/tools/settings/settings/base.py @@ -299,13 +299,18 @@ def _apply_values_from_project_action(self, menu, actions_mapping): if self.entity.is_dynamic_item or self.entity.is_in_dynamic_item: return - submenu = QtWidgets.QMenu("Apply values from", menu) - current_project_name = self.category_widget.project_name + project_names = [] for project_name in self.category_widget.get_project_names(): - if current_project_name == project_name: - continue + if project_name != current_project_name: + project_names.append(project_name) + + if not project_names: + return + + submenu = QtWidgets.QMenu("Apply values from", menu) + for project_name in project_names: if project_name is None: project_name = DEFAULT_PROJECT_LABEL From d3ac116e70554e08397bc644ed3dc5b6fbbcfb5f Mon Sep 17 00:00:00 2001 From: BenoitConnan Date: Wed, 8 Dec 2021 15:09:10 +0100 Subject: [PATCH 137/492] add options to ReferenceLoader "attach_to_root" : wether or not a group should contain reference --- .../hosts/maya/plugins/load/load_reference.py | 115 +++++++++--------- 1 file changed, 59 insertions(+), 56 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_reference.py b/openpype/hosts/maya/plugins/load/load_reference.py index cfe81492187..858eb588b13 100644 --- a/openpype/hosts/maya/plugins/load/load_reference.py +++ b/openpype/hosts/maya/plugins/load/load_reference.py @@ -40,85 +40,88 @@ def process_reference(self, context, name, namespace, options): except ValueError: family = "model" + group_name = "{}:_GRP".format(namespace) + # True by default to keep legacy behaviours + attach_to_root = options.get("attach_to_root", True) + with maya.maintained_selection(): - groupName = "{}:_GRP".format(namespace) cmds.loadPlugin("AbcImport.mll", quiet=True) nodes = cmds.file(self.fname, namespace=namespace, sharedReferenceFile=False, - groupReference=True, - groupName=groupName, reference=True, - returnNewNodes=True) - - # namespace = cmds.referenceQuery(nodes[0], namespace=True) + returnNewNodes=True, + groupReference=attach_to_root, + groupName=group_name) shapes = cmds.ls(nodes, shapes=True, long=True) - newNodes = (list(set(nodes) - set(shapes))) + new_nodes = (list(set(nodes) - set(shapes))) current_namespace = pm.namespaceInfo(currentNamespace=True) if current_namespace != ":": - groupName = current_namespace + ":" + groupName + group_name = current_namespace + ":" + group_name + + self[:] = new_nodes - groupNode = pm.PyNode(groupName) - roots = set() + if attach_to_root: + group_node = pm.PyNode(group_name) + roots = set() - for node in newNodes: - try: - roots.add(pm.PyNode(node).getAllParents()[-2]) - except: # noqa: E722 - pass + for node in new_nodes: + try: + roots.add(pm.PyNode(node).getAllParents()[-2]) + except: # noqa: E722 + pass - if family not in ["layout", "setdress", "mayaAscii", "mayaScene"]: + if family not in [ + "layout", "setdress", "mayaAscii", "mayaScene"]: + for root in roots: + root.setParent(world=True) + + group_node.zeroTransformPivots() for root in roots: - root.setParent(world=True) - - groupNode.zeroTransformPivots() - for root in roots: - root.setParent(groupNode) - - cmds.setAttr(groupName + ".displayHandle", 1) - - settings = get_project_settings(os.environ['AVALON_PROJECT']) - colors = settings['maya']['load']['colors'] - c = colors.get(family) - if c is not None: - groupNode.useOutlinerColor.set(1) - groupNode.outlinerColor.set( - (float(c[0])/255), - (float(c[1])/255), - (float(c[2])/255) - ) - - self[:] = newNodes - - cmds.setAttr(groupName + ".displayHandle", 1) - # get bounding box - bbox = cmds.exactWorldBoundingBox(groupName) - # get pivot position on world space - pivot = cmds.xform(groupName, q=True, sp=True, ws=True) - # center of bounding box - cx = (bbox[0] + bbox[3]) / 2 - cy = (bbox[1] + bbox[4]) / 2 - cz = (bbox[2] + bbox[5]) / 2 - # add pivot position to calculate offset - cx = cx + pivot[0] - cy = cy + pivot[1] - cz = cz + pivot[2] - # set selection handle offset to center of bounding box - cmds.setAttr(groupName + ".selectHandleX", cx) - cmds.setAttr(groupName + ".selectHandleY", cy) - cmds.setAttr(groupName + ".selectHandleZ", cz) + root.setParent(group_node) + + cmds.setAttr(group_name + ".displayHandle", 1) + + settings = get_project_settings(os.environ['AVALON_PROJECT']) + colors = settings['maya']['load']['colors'] + c = colors.get(family) + if c is not None: + group_node.useOutlinerColor.set(1) + group_node.outlinerColor.set( + (float(c[0])/255), + (float(c[1])/255), + (float(c[2])/255)) + + cmds.setAttr(group_name + ".displayHandle", 1) + # get bounding box + bbox = cmds.exactWorldBoundingBox(group_name) + # get pivot position on world space + pivot = cmds.xform(group_name, q=True, sp=True, ws=True) + # center of bounding box + cx = (bbox[0] + bbox[3]) / 2 + cy = (bbox[1] + bbox[4]) / 2 + cz = (bbox[2] + bbox[5]) / 2 + # add pivot position to calculate offset + cx = cx + pivot[0] + cy = cy + pivot[1] + cz = cz + pivot[2] + # set selection handle offset to center of bounding box + cmds.setAttr(group_name + ".selectHandleX", cx) + cmds.setAttr(group_name + ".selectHandleY", cy) + cmds.setAttr(group_name + ".selectHandleZ", cz) if family == "rig": self._post_process_rig(name, namespace, context, options) else: + if "translate" in options: - cmds.setAttr(groupName + ".t", *options["translate"]) + cmds.setAttr(group_name + ".t", *options["translate"]) - return newNodes + return new_nodes def switch(self, container, representation): self.update(container, representation) From f88f1a5306b86e20e14dd17b313739107c782f5b Mon Sep 17 00:00:00 2001 From: BenoitConnan Date: Wed, 8 Dec 2021 15:24:24 +0100 Subject: [PATCH 138/492] hound fix --- openpype/hosts/maya/plugins/load/load_reference.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_reference.py b/openpype/hosts/maya/plugins/load/load_reference.py index 858eb588b13..dd64fd0a16a 100644 --- a/openpype/hosts/maya/plugins/load/load_reference.py +++ b/openpype/hosts/maya/plugins/load/load_reference.py @@ -75,8 +75,8 @@ def process_reference(self, context, name, namespace, options): except: # noqa: E722 pass - if family not in [ - "layout", "setdress", "mayaAscii", "mayaScene"]: + if family not in ["layout", "setdress", + "mayaAscii", "mayaScene"]: for root in roots: root.setParent(world=True) @@ -92,9 +92,9 @@ def process_reference(self, context, name, namespace, options): if c is not None: group_node.useOutlinerColor.set(1) group_node.outlinerColor.set( - (float(c[0])/255), - (float(c[1])/255), - (float(c[2])/255)) + (float(c[0]) / 255), + (float(c[1]) / 255), + (float(c[2]) / 255)) cmds.setAttr(group_name + ".displayHandle", 1) # get bounding box From 6ed4db4da11ae598c8e7ebf3fbf94dc434cfcd47 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Wed, 8 Dec 2021 17:11:45 +0100 Subject: [PATCH 139/492] installand copy xcb libs to pyside2 inside openpype --- Dockerfile.centos7 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Dockerfile.centos7 b/Dockerfile.centos7 index f3b257e66b4..ce60ea7fb19 100644 --- a/Dockerfile.centos7 +++ b/Dockerfile.centos7 @@ -41,6 +41,8 @@ RUN yum -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.n ncurses \ ncurses-devel \ qt5-qtbase-devel \ + xcb-util-wm \ + xcb-util-renderutil && yum clean all # we need to build our own patchelf @@ -92,7 +94,8 @@ RUN source $HOME/.bashrc \ RUN cp /usr/lib64/libffi* ./build/exe.linux-x86_64-3.7/lib \ && cp /usr/lib64/libssl* ./build/exe.linux-x86_64-3.7/lib \ && cp /usr/lib64/libcrypto* ./build/exe.linux-x86_64-3.7/lib \ - && cp /root/.pyenv/versions/${OPENPYPE_PYTHON_VERSION}/lib/libpython* ./build/exe.linux-x86_64-3.7/lib + && cp /root/.pyenv/versions/${OPENPYPE_PYTHON_VERSION}/lib/libpython* ./build/exe.linux-x86_64-3.7/lib \ + && cp /usr/lib64/libxcb* ./build/exe.linux-x86_64-3.7/vendor/python/PySide2/Qt/lib RUN cd /opt/openpype \ rm -rf ./vendor/bin From 0dd3eca360fd10693f3c3a1de6375d55151d7c41 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 8 Dec 2021 17:25:24 +0100 Subject: [PATCH 140/492] added helper method to change style properties --- openpype/tools/settings/settings/base.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/openpype/tools/settings/settings/base.py b/openpype/tools/settings/settings/base.py index f8378ed18cb..bc6c6d10ff2 100644 --- a/openpype/tools/settings/settings/base.py +++ b/openpype/tools/settings/settings/base.py @@ -26,6 +26,14 @@ def __init__(self, category_widget, entity, entity_widget): self.label_widget = None self.create_ui() + @staticmethod + def set_style_property(obj, property_name, property_value): + if obj.property(property_name) == property_value: + return + + obj.setProperty(property_name, property_value) + obj.style().polish(obj) + def scroll_to(self, widget): self.category_widget.scroll_to(widget) From 0479d544bd3c23539c3367ac2dada2d8345c7ad0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 8 Dec 2021 17:25:41 +0100 Subject: [PATCH 141/492] update geo on show of completer view --- openpype/tools/settings/settings/widgets.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openpype/tools/settings/settings/widgets.py b/openpype/tools/settings/settings/widgets.py index 4b88c1f93ff..bfe83392198 100644 --- a/openpype/tools/settings/settings/widgets.py +++ b/openpype/tools/settings/settings/widgets.py @@ -95,6 +95,10 @@ def sizeHint(self): return result + def showEvent(self, event): + super(CompleterView, self).showEvent(event) + self._update_geo() + def _update_geo(self): size_hint = self.sizeHint() self.resize(size_hint.width(), size_hint.height()) From ccce04309bac2318a6ebf87916b0f646e3204dd1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 8 Dec 2021 17:26:33 +0100 Subject: [PATCH 142/492] renamed exception BaseInvalidValueType to BaseInvalidValue --- openpype/settings/entities/__init__.py | 4 ++-- openpype/settings/entities/base_entity.py | 4 ++-- openpype/settings/entities/color_entity.py | 6 +++--- openpype/settings/entities/exceptions.py | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/openpype/settings/entities/__init__.py b/openpype/settings/entities/__init__.py index 4efd3582974..a173e2454fd 100644 --- a/openpype/settings/entities/__init__.py +++ b/openpype/settings/entities/__init__.py @@ -57,7 +57,7 @@ SchemaError, DefaultsNotDefined, StudioDefaultsNotDefined, - BaseInvalidValueType, + BaseInvalidValue, InvalidValueType, InvalidKeySymbols, SchemaMissingFileInfo, @@ -130,7 +130,7 @@ __all__ = ( "DefaultsNotDefined", "StudioDefaultsNotDefined", - "BaseInvalidValueType", + "BaseInvalidValue", "InvalidValueType", "InvalidKeySymbols", "SchemaMissingFileInfo", diff --git a/openpype/settings/entities/base_entity.py b/openpype/settings/entities/base_entity.py index 341968bd75e..12754d345ff 100644 --- a/openpype/settings/entities/base_entity.py +++ b/openpype/settings/entities/base_entity.py @@ -9,7 +9,7 @@ ) from .exceptions import ( - BaseInvalidValueType, + BaseInvalidValue, InvalidValueType, SchemeGroupHierarchyBug, EntitySchemaError @@ -432,7 +432,7 @@ def _check_update_value(self, value, value_source): try: new_value = self.convert_to_valid_type(value) - except BaseInvalidValueType: + except BaseInvalidValue: new_value = NOT_SET if new_value is not NOT_SET: diff --git a/openpype/settings/entities/color_entity.py b/openpype/settings/entities/color_entity.py index 3becf2d8654..bdaab6f583a 100644 --- a/openpype/settings/entities/color_entity.py +++ b/openpype/settings/entities/color_entity.py @@ -1,7 +1,7 @@ from .lib import STRING_TYPE from .input_entities import InputEntity from .exceptions import ( - BaseInvalidValueType, + BaseInvalidValue, InvalidValueType ) @@ -47,7 +47,7 @@ def convert_to_valid_type(self, value): reason = "Color entity expect 4 items in list got {}".format( len(value) ) - raise BaseInvalidValueType(reason, self.path) + raise BaseInvalidValue(reason, self.path) new_value = [] for item in value: @@ -60,7 +60,7 @@ def convert_to_valid_type(self, value): reason = ( "Color entity expect 4 integers in range 0-255 got {}" ).format(value) - raise BaseInvalidValueType(reason, self.path) + raise BaseInvalidValue(reason, self.path) new_value.append(item) # Make sure diff --git a/openpype/settings/entities/exceptions.py b/openpype/settings/entities/exceptions.py index f352c94f20f..d1728a7b124 100644 --- a/openpype/settings/entities/exceptions.py +++ b/openpype/settings/entities/exceptions.py @@ -15,14 +15,14 @@ def __init__(self, obj): super(StudioDefaultsNotDefined, self).__init__(msg) -class BaseInvalidValueType(Exception): +class BaseInvalidValue(Exception): def __init__(self, reason, path): msg = "Path \"{}\". {}".format(path, reason) self.msg = msg - super(BaseInvalidValueType, self).__init__(msg) + super(BaseInvalidValue, self).__init__(msg) -class InvalidValueType(BaseInvalidValueType): +class InvalidValueType(BaseInvalidValue): def __init__(self, valid_types, invalid_type, path): joined_types = ", ".join( [str(valid_type) for valid_type in valid_types] From 703368c5b0eaddc93172d6487ac1b5dcb0e5f91b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 8 Dec 2021 17:27:02 +0100 Subject: [PATCH 143/492] added validation of version value with convert_to_valid_type --- .../settings/entities/op_version_entity.py | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/openpype/settings/entities/op_version_entity.py b/openpype/settings/entities/op_version_entity.py index 2458f038527..20495a0d42c 100644 --- a/openpype/settings/entities/op_version_entity.py +++ b/openpype/settings/entities/op_version_entity.py @@ -2,10 +2,15 @@ op_version_control_available, get_remote_versions, openpype_path_is_set, - openpype_path_is_accessible + openpype_path_is_accessible, + get_OpenPypeVersion ) from .input_entities import TextEntity -from .lib import OverrideState +from .lib import ( + OverrideState, + NOT_SET +) +from .exceptions import BaseInvalidValue class OpenPypeVersionInput(TextEntity): @@ -32,6 +37,21 @@ def set_override_state(self, state, *args, **kwargs): state, *args, **kwargs ) + def convert_to_valid_type(self, value): + if value and value is not NOT_SET: + OpenPypeVersion = get_OpenPypeVersion() + if OpenPypeVersion is not None: + try: + OpenPypeVersion(version=value) + except Exception: + raise BaseInvalidValue( + "Value \"{}\"is not valid version format.".format( + value + ), + self.path + ) + return super(OpenPypeVersionInput, self).convert_to_valid_type(value) + class ProductionVersionsInputEntity(OpenPypeVersionInput): schema_types = ["production-versions-text"] From 9acdf2e338ebd44c4321035b4e885e863c81deab Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 8 Dec 2021 17:27:27 +0100 Subject: [PATCH 144/492] added style to info label --- openpype/style/data.json | 4 +++- openpype/style/style.css | 8 ++++++++ openpype/tools/settings/settings/item_widgets.py | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/openpype/style/data.json b/openpype/style/data.json index 026eaf42648..62573f015e2 100644 --- a/openpype/style/data.json +++ b/openpype/style/data.json @@ -111,7 +111,9 @@ "focus-border": "#839caf", "image-btn": "#bfccd6", "image-btn-hover": "#189aea", - "image-btn-disabled": "#bfccd6" + "image-btn-disabled": "#bfccd6", + "version-exists": "#458056", + "version-not-found": "#ffc671" } } } diff --git a/openpype/style/style.css b/openpype/style/style.css index 0ef15a25113..9249db5f1e1 100644 --- a/openpype/style/style.css +++ b/openpype/style/style.css @@ -1158,6 +1158,14 @@ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { border-radius: 5px; } +#OpenPypeVersionLabel[state="success"] { + color: {color:settings:version-exists}; +} + +#OpenPypeVersionLabel[state="warning"] { + color: {color:settings:version-not-found}; +} + #ShadowWidget { font-size: 36pt; } diff --git a/openpype/tools/settings/settings/item_widgets.py b/openpype/tools/settings/settings/item_widgets.py index 0c661623752..3a7a1079657 100644 --- a/openpype/tools/settings/settings/item_widgets.py +++ b/openpype/tools/settings/settings/item_widgets.py @@ -424,6 +424,7 @@ def __init__(self, *args, **kwargs): def create_ui(self): super(OpenPypeVersionText, self).create_ui() info_widget = QtWidgets.QLabel("Latest", self) + info_widget.setObjectName("OpenPypeVersionLabel") self.content_layout.addWidget(info_widget, 1) self._info_widget = info_widget From 84022f9e0def952a44a7cbea3f35b690705c2c78 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 8 Dec 2021 17:27:53 +0100 Subject: [PATCH 145/492] added more variants of info label with colors and ivalidation --- .../tools/settings/settings/item_widgets.py | 63 +++++++++++++++++-- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/openpype/tools/settings/settings/item_widgets.py b/openpype/tools/settings/settings/item_widgets.py index 3a7a1079657..9f78060c874 100644 --- a/openpype/tools/settings/settings/item_widgets.py +++ b/openpype/tools/settings/settings/item_widgets.py @@ -4,6 +4,7 @@ from openpype.widgets.sliders import NiceSlider from openpype.tools.settings import CHILD_OFFSET +from openpype.settings.entities.exceptions import BaseInvalidValue from .widgets import ( ExpandingWidget, @@ -423,7 +424,7 @@ def __init__(self, *args, **kwargs): def create_ui(self): super(OpenPypeVersionText, self).create_ui() - info_widget = QtWidgets.QLabel("Latest", self) + info_widget = QtWidgets.QLabel(self) info_widget.setObjectName("OpenPypeVersionLabel") self.content_layout.addWidget(info_widget, 1) @@ -431,17 +432,67 @@ def create_ui(self): def _update_info_widget(self): value = self.input_value() - if value == "": - self._info_widget.setText("Latest") + + message = "" + tooltip = "" + state = None + if self._is_invalid: + message = "Invalid version format" + + elif value == "": + message = "Use latest available version" + tooltip = "Latest version from OpenPype zip repository will used" + elif value in self.entity.value_hints: - self._info_widget.setText("Ok") + message = "Version {} will be used".format(value) + state = "success" + else: - self._info_widget.setText("Version not found from this workstation") + message = ( + "Version {} not found in listed versions".format(value) + ) + state = "warning" + if self.entity.value_hints: + tooltip = "Found versions are {}".format(", ".join( + ['"{}"'.format(hint) for hint in self.entity.value_hints] + )) + else: + tooltip = "No versions were found" + + self._info_widget.setText(message) + self._info_widget.setToolTip(tooltip) + self.set_style_property(self._info_widget, "state", state) + + def set_entity_value(self): + super(OpenPypeVersionText, self).set_entity_value() + self._invalidate() + self._update_info_widget() + + def _on_value_change_timer(self): + value = self.input_value() + self._invalidate() + if not self.is_invalid: + self.entity.set(value) + self.update_style() + else: + # Manually trigger hierachical style update + self.ignore_input_changes.set_ignore(True) + self.ignore_input_changes.set_ignore(False) + + self._update_info_widget() + + def _invalidate(self): + value = self.input_value() + try: + self.entity.convert_to_valid_type(value) + is_invalid = False + except BaseInvalidValue: + is_invalid = True + self._is_invalid = is_invalid def _on_entity_change(self): super(OpenPypeVersionText, self)._on_entity_change() self._refresh_completer() - self._update_info_widget() class NumberWidget(InputWidget): From 5181c52d12a2a7e3b2688d01ea7f05df73379d70 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 8 Dec 2021 17:29:49 +0100 Subject: [PATCH 146/492] modified messages a little bit --- openpype/tools/settings/settings/item_widgets.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/openpype/tools/settings/settings/item_widgets.py b/openpype/tools/settings/settings/item_widgets.py index 9f78060c874..22f672da2b4 100644 --- a/openpype/tools/settings/settings/item_widgets.py +++ b/openpype/tools/settings/settings/item_widgets.py @@ -437,27 +437,29 @@ def _update_info_widget(self): tooltip = "" state = None if self._is_invalid: - message = "Invalid version format" + message = "Invalid OpenPype version format" elif value == "": message = "Use latest available version" - tooltip = "Latest version from OpenPype zip repository will used" + tooltip = ( + "Latest version from OpenPype zip repository will be used" + ) elif value in self.entity.value_hints: - message = "Version {} will be used".format(value) state = "success" + message = "Version {} will be used".format(value) else: + state = "warning" message = ( "Version {} not found in listed versions".format(value) ) - state = "warning" if self.entity.value_hints: - tooltip = "Found versions are {}".format(", ".join( + tooltip = "Listed versions: {}".format(", ".join( ['"{}"'.format(hint) for hint in self.entity.value_hints] )) else: - tooltip = "No versions were found" + tooltip = "No versions were listed" self._info_widget.setText(message) self._info_widget.setToolTip(tooltip) From 1588df3ca275165a3fb143ddcd5c9d4b4af92ea8 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 8 Dec 2021 17:37:03 +0100 Subject: [PATCH 147/492] added placeholder inheritance back --- openpype/tools/settings/settings/widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/settings/settings/widgets.py b/openpype/tools/settings/settings/widgets.py index 3e44bd39699..cc6e396db07 100644 --- a/openpype/tools/settings/settings/widgets.py +++ b/openpype/tools/settings/settings/widgets.py @@ -185,7 +185,7 @@ def enter_pressed(self): self._on_activated(index) -class SettingsLineEdit(QtWidgets.QLineEdit): +class SettingsLineEdit(PlaceholderLineEdit): focused_in = QtCore.Signal() def __init__(self, *args, **kwargs): From 9e2760c52e5e0a8964e2ed3f6c1bcb44976408eb Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 8 Dec 2021 18:10:46 +0100 Subject: [PATCH 148/492] OP-2042 - better handling of reusing deployed workfile --- .../hosts/nuke/test_publish_in_nuke.py | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/tests/integration/hosts/nuke/test_publish_in_nuke.py b/tests/integration/hosts/nuke/test_publish_in_nuke.py index d603b9f177a..092fd7d1c6f 100644 --- a/tests/integration/hosts/nuke/test_publish_in_nuke.py +++ b/tests/integration/hosts/nuke/test_publish_in_nuke.py @@ -1,6 +1,7 @@ import pytest import os import logging +import shutil from tests.lib.testing_classes import PublishTest @@ -23,6 +24,8 @@ class TestPublishInNuke(PublishTest): (in cmd with activated {OPENPYPE_ROOT}/.venv) {OPENPYPE_ROOT}/.venv/Scripts/python.exe {OPENPYPE_ROOT}/start.py runtests ../tests/integration/hosts/nuke # noqa: E501 + To check log/errors from launched app's publish process keep PERSIST + to True and check `test_openpype.logs` collection. """ PERSIST = True # True - keep test_db, test_openpype, outputted test files @@ -36,21 +39,32 @@ class TestPublishInNuke(PublishTest): TIMEOUT = 120 # publish timeout - TEST_DATA_FOLDER = None # provide existing folder with test data + TEST_DATA_FOLDER = "C:\\Users\\petrk\\AppData\\Local\\Temp\\tmpbfh976y6" # provide existing folder with test data @pytest.fixture(scope="module") def last_workfile_path(self, download_test_data): """Get last_workfile_path from source data. """ - log.info("log last_workfile_path") - src_path = os.path.join( - download_test_data, - "input", - "workfile", - "test_project_test_asset_CompositingInNuke_v001.nk") - - yield src_path + source_file_name = "test_project_test_asset_CompositingInNuke_v001.nk" + src_path = os.path.join(download_test_data, + "input", + "workfile", + source_file_name) + dest_folder = os.path.join(download_test_data, + self.PROJECT, + self.ASSET, + "work", + self.TASK) + if not os.path.exists(dest_folder): + os.makedirs(dest_folder) + + dest_path = os.path.join(dest_folder, + source_file_name) + + shutil.copy(src_path, dest_path) + + yield dest_path @pytest.fixture(scope="module") def startup_scripts(self, monkeypatch_session, download_test_data): @@ -68,9 +82,9 @@ def test_db_asserts(self, dbcon, publish_finished): """Host and input data dependent expected results in DB.""" print("test_db_asserts") versions = dbcon.count_documents({"type": "version"}) - assert 5 == versions, \ + assert 2 == versions, \ "Not expected no of versions. "\ - "Expected 5, found {}".format(versions) + "Expected 2, found {}".format(versions) assert 0 == dbcon.count_documents({"type": "version", "name": {"$ne": 1}}), \ From a3638ef70ffc2c6d6bed9b8bf0126736f59b35fe Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 8 Dec 2021 18:12:01 +0100 Subject: [PATCH 149/492] OP-2042 - injection of TEST_SOURCE_FOLDER It tells Nuke where it should locate test input data --- tests/lib/testing_classes.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/lib/testing_classes.py b/tests/lib/testing_classes.py index 0a04dc59c2e..25ad66cdfbf 100644 --- a/tests/lib/testing_classes.py +++ b/tests/lib/testing_classes.py @@ -105,9 +105,6 @@ def env_var(self, monkeypatch_session, download_test_data): value = value.format(**all_vars) print("Setting {}:{}".format(key, value)) monkeypatch_session.setenv(key, str(value)) - import openpype - - openpype_root = os.path.dirname(os.path.dirname(openpype.__file__)) #reset connection to openpype DB with new env var import openpype.settings.lib as sett_lib @@ -116,10 +113,16 @@ def env_var(self, monkeypatch_session, download_test_data): sett_lib.create_settings_handler() sett_lib.create_local_settings_handler() + import openpype + openpype_root = os.path.dirname(os.path.dirname(openpype.__file__)) + # ?? why 2 of those monkeypatch_session.setenv("OPENPYPE_ROOT", openpype_root) monkeypatch_session.setenv("OPENPYPE_REPOS_ROOT", openpype_root) + # for remapping purposes (currently in Nuke) + monkeypatch_session.setenv("TEST_SOURCE_FOLDER", download_test_data) + @pytest.fixture(scope="module") def db_setup(self, download_test_data, env_var, monkeypatch_session): """Restore prepared MongoDB dumps into selected DB.""" @@ -271,7 +274,8 @@ def launched_app(self, dbcon, download_test_data, last_workfile_path, app_name = "{}/{}".format(self.APP, variant) - yield application_manager.launch(app_name, **data) + app_process = application_manager.launch(app_name, **data) + yield app_process @pytest.fixture(scope="module") def publish_finished(self, dbcon, launched_app, download_test_data): From e680a362b778943f9cfe80f18395c69c8c5ca8b2 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 8 Dec 2021 18:13:08 +0100 Subject: [PATCH 150/492] OP-2042 - additions to developer documentation --- tests/integration/README.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/integration/README.md b/tests/integration/README.md index 8839e2e43f6..0b6a1804aec 100644 --- a/tests/integration/README.md +++ b/tests/integration/README.md @@ -15,6 +15,10 @@ How to run OR can use built executables `openpype_console runtests {ABS_PATH}/tests/integration` +How to check logs/errors from app +-------------------------------- +Keep PERSIST to True in the class and check `test_openpype.logs` collection. + How to create test for publishing from host ------------------------------------------ - Extend PublishTest in `tests/lib/testing_classes.py` @@ -28,9 +32,21 @@ How to create test for publishing from host ``` import openpype from avalon import api, HOST + +from openpype.api import Logger + +log = Logger().get_logger(__name__) api.install(HOST) -pyblish.util.publish() +log_lines = [] +for result in pyblish.util.publish_iter(): + for record in result["records"]: # for logging to test_openpype DB + log_lines.append("{}: {}".format( + result["plugin"].label, record.msg)) + + if result["error"]: + err_fmt = "Failed {plugin.__name__}: {error} -- {error.traceback}" + log.error(err_fmt.format(**result)) EXIT_APP (command to exit host) ``` From 767ef9d715c92f50493a1d20352e3df5ecaff63a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 8 Dec 2021 18:31:14 +0100 Subject: [PATCH 151/492] sort versions earlier --- start.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/start.py b/start.py index 32fa4bd062c..55008a9526d 100644 --- a/start.py +++ b/start.py @@ -662,6 +662,8 @@ def _find_frozen_openpype(use_version: str = None, if local_version not in openpype_versions: openpype_versions.append(local_version) + openpype_versions.sort() + # Find OpenPype version that should be used openpype_version = None if use_version is not None: @@ -694,7 +696,6 @@ def _find_frozen_openpype(use_version: str = None, else: # Use latest available version _print("Finding latest version") - openpype_versions.sort() openpype_version = openpype_versions[-1] # get local frozen version and add it to detected version so if it is From ce204157273e74fd15abd9a37eafbd7722e533a7 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 8 Dec 2021 18:31:40 +0100 Subject: [PATCH 152/492] added option to pass "latest" to use version argument --- start.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/start.py b/start.py index 55008a9526d..28c04600c40 100644 --- a/start.py +++ b/start.py @@ -667,13 +667,17 @@ def _find_frozen_openpype(use_version: str = None, # Find OpenPype version that should be used openpype_version = None if use_version is not None: - # Version was specified with arguments or env OPENPYPE_VERSION - # - should crash if version is not available - _print("Finding specified version \"{}\"".format(use_version)) - for version in openpype_versions: - if version == use_version: - openpype_version = version - break + if use_version.lower() == "latest": + openpype_version = openpype_versions[-1] + else: + use_version_obj = OpenPypeVersion(use_version) + # Version was specified with arguments or env OPENPYPE_VERSION + # - should crash if version is not available + _print("Finding specified version \"{}\"".format(use_version)) + for version in openpype_versions: + if version == use_version_obj: + openpype_version = version + break if openpype_version is None: raise OpenPypeVersionNotFound( From 52c5424f2b59987876eb0412dfbdab2c8af0cd09 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 8 Dec 2021 18:31:50 +0100 Subject: [PATCH 153/492] modified messages --- start.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/start.py b/start.py index 28c04600c40..6fa624b5cd7 100644 --- a/start.py +++ b/start.py @@ -681,7 +681,9 @@ def _find_frozen_openpype(use_version: str = None, if openpype_version is None: raise OpenPypeVersionNotFound( - "Requested version \"{}\" was not found.".format(use_version) + "Requested version \"{}\" was not found.".format( + use_version + ) ) elif studio_version: @@ -695,7 +697,7 @@ def _find_frozen_openpype(use_version: str = None, raise OpenPypeVersionNotFound(( "Requested OpenPype version \"{}\" defined by settings" " was not found." - ).format(use_version)) + ).format(studio_version)) else: # Use latest available version From 97404abf43adc4ebb38afa1e93403e7d16776b97 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 8 Dec 2021 19:00:12 +0100 Subject: [PATCH 154/492] OP-2053 - added check of installed extension for PS --- .../publish/collect_extension_version.py | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 openpype/hosts/photoshop/plugins/publish/collect_extension_version.py diff --git a/openpype/hosts/photoshop/plugins/publish/collect_extension_version.py b/openpype/hosts/photoshop/plugins/publish/collect_extension_version.py new file mode 100644 index 00000000000..f07ff0b0ff7 --- /dev/null +++ b/openpype/hosts/photoshop/plugins/publish/collect_extension_version.py @@ -0,0 +1,57 @@ +import os +import re +import pyblish.api + +from avalon import photoshop + + +class CollectExtensionVersion(pyblish.api.ContextPlugin): + """ Pulls and compares version of installed extension. + + It is recommended to use same extension as in provided Openpype code. + + Please use Anastasiy’s Extension Manager or ZXPInstaller to update + extension in case of an error. + + You can locate extension.zxp in your installed Openpype code in + `repos/avalon-core/avalon/photoshop` + """ + # This technically should be a validator, but other collectors might be + # impacted with usage of obsolete extension, so collector that runs first + # was chosen + order = pyblish.api.CollectorOrder - 0.5 + label = "Collect extension version" + hosts = ["photoshop"] + + optional = True + active = True + + def process(self, context): + installed_version = photoshop.stub().get_extension_version() + + if not installed_version: + raise ValueError("Unknown version, probably old extension") + + manifest_url = os.path.join(os.path.dirname(photoshop.__file__), + "extension", "CSXS", "manifest.xml") + + if not os.path.exists(manifest_url): + self.log.debug("Unable to locate extension manifest, not checking") + return + + expected_version = None + with open(manifest_url) as fp: + content = fp.read() + + found = re.findall(r'(ExtensionBundleVersion=")([0-10\.]+)(")', + content) + if found: + expected_version = found[0][1] + + if expected_version != installed_version: + msg = "Expected version '{}' found '{}'\n".format( + expected_version, installed_version) + msg += "Please update your installed extension, it might not work " + msg += "properly." + + raise ValueError(msg) From 5dc2fd05885718cb6c90083f14f60de18cc2c828 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 8 Dec 2021 19:00:44 +0100 Subject: [PATCH 155/492] OP-2053 - bump down order of collector for current file It depends on valid extension --- .../hosts/photoshop/plugins/publish/collect_current_file.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/photoshop/plugins/publish/collect_current_file.py b/openpype/hosts/photoshop/plugins/publish/collect_current_file.py index 3cc3e3f636c..4d4829555ec 100644 --- a/openpype/hosts/photoshop/plugins/publish/collect_current_file.py +++ b/openpype/hosts/photoshop/plugins/publish/collect_current_file.py @@ -8,7 +8,7 @@ class CollectCurrentFile(pyblish.api.ContextPlugin): """Inject the current working file into context""" - order = pyblish.api.CollectorOrder - 0.5 + order = pyblish.api.CollectorOrder - 0.49 label = "Current File" hosts = ["photoshop"] From d2edf3da743459bf3b043d5c855098b04cb66e79 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 8 Dec 2021 19:02:37 +0100 Subject: [PATCH 156/492] don't return latest version --- igniter/bootstrap_repos.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/igniter/bootstrap_repos.py b/igniter/bootstrap_repos.py index 9446b3e8ce2..3db9dd0b915 100644 --- a/igniter/bootstrap_repos.py +++ b/igniter/bootstrap_repos.py @@ -589,7 +589,7 @@ def get_expected_studio_version(cls, staging=False): """ result = get_expected_studio_version_str(staging) if not result: - return cls.get_latest_version(staging, False) + return None return OpenPypeVersion(version=result) From 02b8e57a81a2b4c9782dc69a57e7b53c29598d8a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 8 Dec 2021 19:03:16 +0100 Subject: [PATCH 157/492] return None from get_latest_version if there are any openpype versions available --- igniter/bootstrap_repos.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/igniter/bootstrap_repos.py b/igniter/bootstrap_repos.py index 3db9dd0b915..163db07f595 100644 --- a/igniter/bootstrap_repos.py +++ b/igniter/bootstrap_repos.py @@ -559,7 +559,8 @@ def get_latest_version( staging: bool = False, local: bool = False) -> OpenPypeVersion: """Get latest available version. - This is utility version to get latest version from all found. + This is utility version to get latest version from all found except + build. Args: staging (bool, optional): List staging versions if True. @@ -572,6 +573,8 @@ def get_latest_version( openpype_versions = OpenPypeVersion.get_available_versions( staging, local) + if not openpype_versions: + return None return openpype_versions[-1] @classmethod From a160e2229068007f28a320800c0d983784f11405 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 8 Dec 2021 19:03:29 +0100 Subject: [PATCH 158/492] have ability to get build version --- igniter/bootstrap_repos.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/igniter/bootstrap_repos.py b/igniter/bootstrap_repos.py index 163db07f595..920eb5fa6bc 100644 --- a/igniter/bootstrap_repos.py +++ b/igniter/bootstrap_repos.py @@ -554,6 +554,19 @@ def get_versions_from_directory(openpype_dir: Path) -> List: return sorted(_openpype_versions) + @staticmethod + def get_build_version(): + """Get version of OpenPype inside build.""" + openpype_root = Path(os.environ["OPENPYPE_ROOT"]) + build_version_str = BootstrapRepos.get_version(openpype_root) + build_version = None + if build_version_str: + build_version = OpenPypeVersion( + version=build_version_str, + path=openpype_root + ) + return build_version + @staticmethod def get_latest_version( staging: bool = False, local: bool = False) -> OpenPypeVersion: From 406ece7e44db78e44d365632ec6ac1ddaf8494fe Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 8 Dec 2021 19:03:55 +0100 Subject: [PATCH 159/492] use new methods in start.py --- start.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/start.py b/start.py index 6fa624b5cd7..80baff5496a 100644 --- a/start.py +++ b/start.py @@ -651,16 +651,11 @@ def _find_frozen_openpype(use_version: str = None, include_zips=True, staging=use_staging ) - openpype_root = Path(os.environ["OPENPYPE_ROOT"]) - local_version_str = bootstrap.get_version(openpype_root) + local_version = OpenPypeVersion.get_build_version() + if local_version not in openpype_versions: + openpype_versions.append(local_version) + studio_version = OpenPypeVersion.get_expected_studio_version(use_staging) - if local_version_str: - local_version = OpenPypeVersion( - version=local_version_str, - path=openpype_root - ) - if local_version not in openpype_versions: - openpype_versions.append(local_version) openpype_versions.sort() @@ -668,6 +663,7 @@ def _find_frozen_openpype(use_version: str = None, openpype_version = None if use_version is not None: if use_version.lower() == "latest": + _print("Finding latest version") openpype_version = openpype_versions[-1] else: use_version_obj = OpenPypeVersion(use_version) From 715e1a6d32e19fe53ecd3c2061fac6cd87549f04 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 8 Dec 2021 19:06:16 +0100 Subject: [PATCH 160/492] cache build version is it probably won't change during process time --- igniter/bootstrap_repos.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/igniter/bootstrap_repos.py b/igniter/bootstrap_repos.py index 920eb5fa6bc..5e8efd1cc46 100644 --- a/igniter/bootstrap_repos.py +++ b/igniter/bootstrap_repos.py @@ -63,6 +63,7 @@ class OpenPypeVersion(semver.VersionInfo): staging = False path = None _VERSION_REGEX = re.compile(r"(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)(?:-(?P(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$") # noqa: E501 + _build_version = None def __init__(self, *args, **kwargs): """Create OpenPype version. @@ -554,18 +555,20 @@ def get_versions_from_directory(openpype_dir: Path) -> List: return sorted(_openpype_versions) - @staticmethod - def get_build_version(): + @classmethod + def get_build_version(cls): """Get version of OpenPype inside build.""" - openpype_root = Path(os.environ["OPENPYPE_ROOT"]) - build_version_str = BootstrapRepos.get_version(openpype_root) - build_version = None - if build_version_str: - build_version = OpenPypeVersion( - version=build_version_str, - path=openpype_root - ) - return build_version + if cls._build_version is None: + openpype_root = Path(os.environ["OPENPYPE_ROOT"]) + build_version_str = BootstrapRepos.get_version(openpype_root) + build_version = None + if build_version_str: + build_version = OpenPypeVersion( + version=build_version_str, + path=openpype_root + ) + cls._build_version = build_version + return cls._build_version @staticmethod def get_latest_version( From 02686ff096cb3dafd51aa74d1d288c6b7c556506 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 8 Dec 2021 19:31:29 +0100 Subject: [PATCH 161/492] added get build version --- openpype/lib/openpype_version.py | 10 ++++++++++ openpype/settings/entities/op_version_entity.py | 11 ++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/openpype/lib/openpype_version.py b/openpype/lib/openpype_version.py index 42ee4543787..7d9dc6ef941 100644 --- a/openpype/lib/openpype_version.py +++ b/openpype/lib/openpype_version.py @@ -20,6 +20,16 @@ def op_version_control_available(): return True +def get_build_version(): + """Get OpenPype version inside build. + + This version is not returned by any other functions here. + """ + if op_version_control_available(): + return get_OpenPypeVersion().get_build_version() + return None + + def get_available_versions(*args, **kwargs): """Get list of available versions.""" if op_version_control_available(): diff --git a/openpype/settings/entities/op_version_entity.py b/openpype/settings/entities/op_version_entity.py index 20495a0d42c..cf2150f12ec 100644 --- a/openpype/settings/entities/op_version_entity.py +++ b/openpype/settings/entities/op_version_entity.py @@ -3,7 +3,8 @@ get_remote_versions, openpype_path_is_set, openpype_path_is_accessible, - get_OpenPypeVersion + get_OpenPypeVersion, + get_build_version ) from .input_entities import TextEntity from .lib import ( @@ -57,11 +58,15 @@ class ProductionVersionsInputEntity(OpenPypeVersionInput): schema_types = ["production-versions-text"] def _get_openpype_versions(self): - return get_remote_versions(production=True) + versions = get_remote_versions(staging=False, production=True) + versions.append(get_build_version()) + return sorted(versions) class StagingVersionsInputEntity(OpenPypeVersionInput): schema_types = ["staging-versions-text"] def _get_openpype_versions(self): - return get_remote_versions(staging=True) + versions = get_remote_versions(staging=True, production=False) + versions.append(get_build_version()) + return sorted(versions) From b3c1bbd84ee31795a3425d5fd245c2d639b8b58f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 8 Dec 2021 19:31:36 +0100 Subject: [PATCH 162/492] removed unused imports --- openpype/settings/entities/enum_entity.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/openpype/settings/entities/enum_entity.py b/openpype/settings/entities/enum_entity.py index 7c8721556f1..fb6099e82aa 100644 --- a/openpype/settings/entities/enum_entity.py +++ b/openpype/settings/entities/enum_entity.py @@ -1,10 +1,4 @@ import copy -from openpype.lib.openpype_version import ( - op_version_control_available, - get_remote_versions, - openpype_path_is_set, - openpype_path_is_accessible -) from .input_entities import InputEntity from .exceptions import EntitySchemaError from .lib import ( From c05ec98b6049202aacb3273ed2d5ea38b97b5e00 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 8 Dec 2021 19:32:07 +0100 Subject: [PATCH 163/492] removed current verions functions and replace then with get_expected_studio_version --- openpype/lib/openpype_version.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/openpype/lib/openpype_version.py b/openpype/lib/openpype_version.py index 7d9dc6ef941..9a92454ecab 100644 --- a/openpype/lib/openpype_version.py +++ b/openpype/lib/openpype_version.py @@ -2,7 +2,11 @@ Access to logic from igniter is available only for OpenPype processes. Is meant to be able check OpenPype versions for studio. The logic is dependent -on igniter's logic of processing. +on igniter's inner logic of versions. + +Keep in mind that all functions except 'get_build_version' does not return +OpenPype version located in build but versions available in remote versions +repository or locally available. """ import sys @@ -40,42 +44,42 @@ def get_available_versions(*args, **kwargs): def openpype_path_is_set(): + """OpenPype repository path is set in settings.""" if op_version_control_available(): return get_OpenPypeVersion().openpype_path_is_set() return None def openpype_path_is_accessible(): + """OpenPype version repository path can be accessed.""" if op_version_control_available(): return get_OpenPypeVersion().openpype_path_is_accessible() return None def get_local_versions(*args, **kwargs): + """OpenPype versions available on this workstation.""" if op_version_control_available(): return get_OpenPypeVersion().get_local_versions(*args, **kwargs) return None def get_remote_versions(*args, **kwargs): + """OpenPype versions in repository path.""" if op_version_control_available(): return get_OpenPypeVersion().get_remote_versions(*args, **kwargs) return None def get_latest_version(*args, **kwargs): + """Get latest version from repository path.""" if op_version_control_available(): return get_OpenPypeVersion().get_latest_version(*args, **kwargs) return None -def get_current_production_version(): - if op_version_control_available(): - return get_OpenPypeVersion().get_production_version() - return None - - -def get_current_staging_version(): +def get_expected_studio_version(staging=False): + """Expected production or staging version in studio.""" if op_version_control_available(): - return get_OpenPypeVersion().get_staging_version() + return get_OpenPypeVersion().get_expected_studio_version(staging) return None From 1a60f7fa0f047132c79a9a148b4ca8d1a474977e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 9 Dec 2021 11:33:32 +0100 Subject: [PATCH 164/492] keep previous find_openpype implementation --- igniter/bootstrap_repos.py | 60 +++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 8 deletions(-) diff --git a/igniter/bootstrap_repos.py b/igniter/bootstrap_repos.py index 5e8efd1cc46..9e58f5bad20 100644 --- a/igniter/bootstrap_repos.py +++ b/igniter/bootstrap_repos.py @@ -1115,21 +1115,65 @@ def add_paths_from_directory(directory: Path) -> None: @staticmethod def find_openpype( + self, openpype_path: Union[Path, str] = None, staging: bool = False, include_zips: bool = False) -> Union[List[OpenPypeVersion], None]: + """Get ordered dict of detected OpenPype version. - if openpype_path: - openpype_versions = OpenPypeVersion.get_versions_from_directory( - openpype_path) - # filter out staging + Resolution order for OpenPype is following: - openpype_versions = [ - v for v in openpype_versions if v.is_staging() == staging - ] + 1) First we test for ``OPENPYPE_PATH`` environment variable + 2) We try to find ``openPypePath`` in registry setting + 3) We use user data directory + + Args: + openpype_path (Path or str, optional): Try to find OpenPype on + the given path or url. + staging (bool, optional): Filter only staging version, skip them + otherwise. + include_zips (bool, optional): If set True it will try to find + OpenPype in zip files in given directory. + + Returns: + dict of Path: Dictionary of detected OpenPype version. + Key is version, value is path to zip file. + None: if OpenPype is not found. + + Todo: + implement git/url support as OpenPype location, so it would be + possible to enter git url, OpenPype would check it out and if it is + ok install it as normal version. + + """ + if openpype_path and not isinstance(openpype_path, Path): + raise NotImplementedError( + ("Finding OpenPype in non-filesystem locations is" + " not implemented yet.")) + + dir_to_search = self.data_dir + user_versions = self.get_openpype_versions(self.data_dir, staging) + # if we have openpype_path specified, search only there. + if openpype_path: + dir_to_search = openpype_path else: - openpype_versions = OpenPypeVersion.get_available_versions(staging) + if os.getenv("OPENPYPE_PATH"): + if Path(os.getenv("OPENPYPE_PATH")).exists(): + dir_to_search = Path(os.getenv("OPENPYPE_PATH")) + else: + try: + registry_dir = Path( + str(self.registry.get_item("openPypePath"))) + if registry_dir.exists(): + dir_to_search = registry_dir + + except ValueError: + # nothing found in registry, we'll use data dir + pass + + openpype_versions = self.get_openpype_versions(dir_to_search, staging) + openpype_versions += user_versions # remove zip file version if needed. if not include_zips: From 77730e8d2b3b2e13026e77a618271936e5a2a7b5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 9 Dec 2021 11:35:03 +0100 Subject: [PATCH 165/492] added new methods to find openpype versions --- igniter/bootstrap_repos.py | 58 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/igniter/bootstrap_repos.py b/igniter/bootstrap_repos.py index 9e58f5bad20..a258f71a20d 100644 --- a/igniter/bootstrap_repos.py +++ b/igniter/bootstrap_repos.py @@ -1114,6 +1114,64 @@ def add_paths_from_directory(directory: Path) -> None: os.environ["PYTHONPATH"] = os.pathsep.join(paths) @staticmethod + def find_openpype_version(version, staging): + if isinstance(version, str): + version = OpenPypeVersion(version=version) + + build_version = OpenPypeVersion.get_build_version() + if build_version == version: + return build_version + + local_versions = OpenPypeVersion.get_local_versions( + staging=staging, production=not staging + ) + zip_version = None + for local_version in local_versions: + if local_version == version: + if local_version.suffix.lower() == ".zip": + zip_version = local_version + else: + return local_version + + if zip_version is not None: + return zip_version + + remote_versions = OpenPypeVersion.get_remote_versions( + staging=staging, production=not staging + ) + for remote_version in remote_versions: + if remote_version == version: + return remote_version + return None + + @staticmethod + def find_latest_openpype_version(staging): + build_version = OpenPypeVersion.get_build_version() + local_versions = OpenPypeVersion.get_local_versions( + staging=staging + ) + remote_versions = OpenPypeVersion.get_remote_versions( + staging=staging + ) + all_versions = local_versions + remote_versions + if not staging: + all_versions.append(build_version) + + if not all_versions: + return None + + all_versions.sort() + latest_version = all_versions[-1] + if latest_version == build_version: + return latest_version + + if not latest_version.path.is_dir(): + for version in local_versions: + if version == latest_version and version.path.is_dir(): + latest_version = version + break + return latest_version + def find_openpype( self, openpype_path: Union[Path, str] = None, From d22214415c17a8037353406c90a509954dcce8ac Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 9 Dec 2021 11:59:57 +0100 Subject: [PATCH 166/492] simplified get build version --- igniter/bootstrap_repos.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/igniter/bootstrap_repos.py b/igniter/bootstrap_repos.py index a258f71a20d..7a367e6f09a 100644 --- a/igniter/bootstrap_repos.py +++ b/igniter/bootstrap_repos.py @@ -561,13 +561,11 @@ def get_build_version(cls): if cls._build_version is None: openpype_root = Path(os.environ["OPENPYPE_ROOT"]) build_version_str = BootstrapRepos.get_version(openpype_root) - build_version = None if build_version_str: - build_version = OpenPypeVersion( + cls._build_version = OpenPypeVersion( version=build_version_str, path=openpype_root ) - cls._build_version = build_version return cls._build_version @staticmethod From bc301fc7893551767763545e689d6ade875da428 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 9 Dec 2021 12:02:19 +0100 Subject: [PATCH 167/492] modified get latest versions --- igniter/bootstrap_repos.py | 54 ++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/igniter/bootstrap_repos.py b/igniter/bootstrap_repos.py index 7a367e6f09a..7de4c5db4b2 100644 --- a/igniter/bootstrap_repos.py +++ b/igniter/bootstrap_repos.py @@ -570,26 +570,58 @@ def get_build_version(cls): @staticmethod def get_latest_version( - staging: bool = False, local: bool = False) -> OpenPypeVersion: + staging: bool = False, + local: bool = None, + remote: bool = None + ) -> OpenPypeVersion: """Get latest available version. - This is utility version to get latest version from all found except - build. + The version does not contain information about path and source. + + This is utility version to get latest version from all found. Build + version is not listed if staging is enabled. + + Arguments 'local' and 'remote' define if local and remote repository + versions are used. All versions are used if both are not set (or set + to 'None'). If only one of them is set to 'True' the other is disabled. + It is possible to set both to 'True' (same as both set to None) and to + 'False' in that case only build version can be used. Args: staging (bool, optional): List staging versions if True. - local (bool, optional): List only local versions. + local (bool, optional): List local versions if True. + remote (bool, optional): List remote versions if True. + """ + if local is None and remote is None: + local = True + remote = True - See also: - OpenPypeVersion.get_available_versions() + elif local is None and not remote: + local = True - """ - openpype_versions = OpenPypeVersion.get_available_versions( - staging, local) + elif remote is None and not local: + remote = True - if not openpype_versions: + build_version = OpenPypeVersion.get_build_version() + local_versions = [] + remote_versions = [] + if local: + local_versions = OpenPypeVersion.get_local_versions( + staging=staging + ) + if remote: + remote_versions = OpenPypeVersion.get_remote_versions( + staging=staging + ) + all_versions = local_versions + remote_versions + if not staging: + all_versions.append(build_version) + + if not all_versions: return None - return openpype_versions[-1] + + all_versions.sort() + return all_versions[-1] @classmethod def get_expected_studio_version(cls, staging=False): From 105e6241b46da79102d50fb84c7376155478a31a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 9 Dec 2021 12:02:32 +0100 Subject: [PATCH 168/492] fixed zip check --- igniter/bootstrap_repos.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/igniter/bootstrap_repos.py b/igniter/bootstrap_repos.py index 7de4c5db4b2..2f49fee064c 100644 --- a/igniter/bootstrap_repos.py +++ b/igniter/bootstrap_repos.py @@ -1158,7 +1158,7 @@ def find_openpype_version(version, staging): zip_version = None for local_version in local_versions: if local_version == version: - if local_version.suffix.lower() == ".zip": + if local_version.path.suffix.lower() == ".zip": zip_version = local_version else: return local_version From 3e04f294e09d98102a673ebc5182258a07866070 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 9 Dec 2021 12:02:44 +0100 Subject: [PATCH 169/492] removed unused method --- igniter/bootstrap_repos.py | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/igniter/bootstrap_repos.py b/igniter/bootstrap_repos.py index 2f49fee064c..77fcd15f1e5 100644 --- a/igniter/bootstrap_repos.py +++ b/igniter/bootstrap_repos.py @@ -477,39 +477,6 @@ def get_remote_versions( filtered_versions.append(version) return list(sorted(set(filtered_versions))) - @classmethod - def get_available_versions( - cls, staging: bool = False, local: bool = False) -> List: - """Get ordered dict of detected OpenPype version. - - Resolution order for OpenPype is following: - - 1) First we test for ``OPENPYPE_PATH`` environment variable - 2) We try to find ``openPypePath`` in registry setting - 3) We use user data directory - - Only versions from 3) will be listed when ``local`` is set to True. - - Args: - staging (bool, optional): List staging versions if True. - local (bool, optional): List only local versions. - - """ - user_versions = cls.get_local_versions() - # if we have openpype_path specified, search only there. - openpype_versions = [] - if not local: - openpype_versions = cls.get_remote_versions() - openpype_versions += user_versions - - # remove duplicates and staging/production - openpype_versions = [ - v for v in openpype_versions if v.is_staging() == staging - ] - openpype_versions = sorted(list(set(openpype_versions))) - - return openpype_versions - @staticmethod def get_versions_from_directory(openpype_dir: Path) -> List: """Get all detected OpenPype versions in directory. From d71b8e2e40d2215a7b7c644d2debbf6549e7e171 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 9 Dec 2021 12:03:23 +0100 Subject: [PATCH 170/492] modified use version argument handling to be able pass "latest" --- start.py | 46 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/start.py b/start.py index 80baff5496a..29afb759c56 100644 --- a/start.py +++ b/start.py @@ -467,23 +467,41 @@ def _process_arguments() -> tuple: use_version = None use_staging = False commands = [] + + # OpenPype version specification through arguments + use_version_arg = "--use-version" + for arg in sys.argv: - if arg == "--use-version": - _print("!!! Please use option --use-version like:") - _print(" --use-version=3.0.0") - sys.exit(1) + if arg.startswith(use_version_arg): + # Remove arg from sys argv + sys.argv.remove(arg) + # Extract string after use version arg + use_version_value = arg[len(use_version_arg):] + + if ( + not use_version_value + or not use_version_value.startswith("=") + ): + _print("!!! Please use option --use-version like:") + _print(" --use-version=3.0.0") + sys.exit(1) - if arg.startswith("--use-version="): - m = re.search( - r"--use-version=(?P\d+\.\d+\.\d+(?:\S*)?)", arg) - if m and m.group('version'): - use_version = m.group('version') - _print(">>> Requested version [ {} ]".format(use_version)) - sys.argv.remove(arg) - if "+staging" in use_version: - use_staging = True - break + version_str = use_version_value[1:] + use_version = None + if version_str.lower() == "latest": + use_version = "latest" else: + m = re.search( + r"(?P\d+\.\d+\.\d+(?:\S*)?)", version_str + ) + if m and m.group('version'): + use_version = m.group('version') + _print(">>> Requested version [ {} ]".format(use_version)) + if "+staging" in use_version: + use_staging = True + break + + if use_version is None: _print("!!! Requested version isn't in correct format.") _print((" Use --list-versions to find out" " proper version string.")) From 27df5b5d924b18c0fcd8c0ae321a3464070e37c7 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 9 Dec 2021 12:03:50 +0100 Subject: [PATCH 171/492] use new methods in bootstrap from code --- start.py | 68 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/start.py b/start.py index 29afb759c56..674cc66a0f9 100644 --- a/start.py +++ b/start.py @@ -781,6 +781,13 @@ def _bootstrap_from_code(use_version, use_staging): # run through repos and add them to `sys.path` and `PYTHONPATH` # set root _openpype_root = OPENPYPE_ROOT + # Unset use version if latest should be used + # - when executed from code then code is expected as latest + # - when executed from build then build is already marked as latest + # in '_find_frozen_openpype' + if use_version and use_version.lower() == "latest": + use_version = None + if getattr(sys, 'frozen', False): local_version = bootstrap.get_version(Path(_openpype_root)) switch_str = f" - will switch to {use_version}" if use_version else "" @@ -790,43 +797,35 @@ def _bootstrap_from_code(use_version, use_staging): # get current version of OpenPype local_version = bootstrap.get_local_live_version() - openpype_versions = bootstrap.find_openpype( - include_zips=True, staging=use_staging) - if use_staging and not use_version: - if not openpype_versions: - raise OpenPypeVersionNotFound( - "Didn't find any staging versions." + # All cases when should be used different version than build + if (use_version and use_version != local_version) or use_staging: + if use_version: + # Explicit version should be used + version_to_use = bootstrap.find_openpype_version( + use_version, use_staging ) - - # Use last found staging version - version_to_use = openpype_versions[-1] - if version_to_use.path.is_file(): - version_to_use.path = bootstrap.extract_openpype( - version_to_use) - bootstrap.add_paths_from_directory(version_to_use.path) - os.environ["OPENPYPE_VERSION"] = str(version_to_use) - version_path = version_to_use.path - os.environ["OPENPYPE_REPOS_ROOT"] = ( - version_path / "openpype" - ).as_posix() - _openpype_root = version_to_use.path.as_posix() - - elif use_version and use_version != local_version: - version_to_use = None - for version in openpype_versions: - if str(version) == use_version: - version_to_use = version - break - - if version_to_use is None: - raise OpenPypeVersionNotFound( - "Requested version \"{}\" was not found.".format(use_version) + if version_to_use is None: + raise OpenPypeVersionNotFound( + "Requested version \"{}\" was not found.".format( + use_version + ) + ) + else: + # Staging version should be used + version_to_use = bootstrap.find_latest_openpype_version( + use_staging ) - - # use specified + if version_to_use is None: + if use_staging: + reason = "Didn't find any staging versions." + else: + # This reason is backup for possible bug in code + reason = "Didn't find any versions." + raise OpenPypeVersionNotFound(reason) + + # Start extraction of version if needed if version_to_use.path.is_file(): - version_to_use.path = bootstrap.extract_openpype( - version_to_use) + version_to_use.path = bootstrap.extract_openpype(version_to_use) bootstrap.add_paths_from_directory(version_to_use.path) os.environ["OPENPYPE_VERSION"] = use_version version_path = version_to_use.path @@ -834,6 +833,7 @@ def _bootstrap_from_code(use_version, use_staging): version_path / "openpype" ).as_posix() _openpype_root = version_to_use.path.as_posix() + else: os.environ["OPENPYPE_VERSION"] = local_version version_path = Path(_openpype_root) From d219ff0cd18219051a00de65ccfa9c6990d94e5b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 9 Dec 2021 12:04:13 +0100 Subject: [PATCH 172/492] use new methods in find frozen openpype --- start.py | 59 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/start.py b/start.py index 674cc66a0f9..58c88df340e 100644 --- a/start.py +++ b/start.py @@ -665,33 +665,25 @@ def _find_frozen_openpype(use_version: str = None, """ # Collect OpenPype versions - openpype_versions = bootstrap.find_openpype( - include_zips=True, - staging=use_staging - ) - local_version = OpenPypeVersion.get_build_version() - if local_version not in openpype_versions: - openpype_versions.append(local_version) - + build_version = OpenPypeVersion.get_build_version() + # Expected version that should be used by studio settings + # - this option is used only if version is not explictly set and if + # studio has set explicit version in settings studio_version = OpenPypeVersion.get_expected_studio_version(use_staging) - openpype_versions.sort() - - # Find OpenPype version that should be used - openpype_version = None if use_version is not None: + # Specific version is defined if use_version.lower() == "latest": - _print("Finding latest version") - openpype_version = openpype_versions[-1] + # Version says to use latest version + _print("Finding latest version defined by use version") + openpype_version = bootstrap.find_latest_openpype_version( + use_staging + ) else: - use_version_obj = OpenPypeVersion(use_version) - # Version was specified with arguments or env OPENPYPE_VERSION - # - should crash if version is not available _print("Finding specified version \"{}\"".format(use_version)) - for version in openpype_versions: - if version == use_version_obj: - openpype_version = version - break + openpype_version = bootstrap.find_openpype_version( + use_version, use_staging + ) if openpype_version is None: raise OpenPypeVersionNotFound( @@ -700,13 +692,12 @@ def _find_frozen_openpype(use_version: str = None, ) ) - elif studio_version: + elif studio_version is not None: + # Studio has defined a version to use _print("Finding studio version \"{}\"".format(studio_version)) - # Look for version specified by settings - for version in openpype_versions: - if version == studio_version: - openpype_version = version - break + openpype_version = bootstrap.find_openpype_version( + studio_version, use_staging + ) if openpype_version is None: raise OpenPypeVersionNotFound(( "Requested OpenPype version \"{}\" defined by settings" @@ -714,13 +705,21 @@ def _find_frozen_openpype(use_version: str = None, ).format(studio_version)) else: - # Use latest available version + # Default behavior to use latest version _print("Finding latest version") - openpype_version = openpype_versions[-1] + openpype_version = bootstrap.find_latest_openpype_version( + use_staging + ) + if openpype_version is None: + if use_staging: + reason = "Didn't find any staging versions." + else: + reason = "Didn't find any versions." + raise OpenPypeVersionNotFound(reason) # get local frozen version and add it to detected version so if it is # newer it will be used instead. - if local_version == openpype_version: + if build_version == openpype_version: version_path = _bootstrap_from_code(use_version, use_staging) openpype_version = OpenPypeVersion( version=BootstrapRepos.get_version(version_path), From a82bf00d7a28242aa0bc134b4220a806cd108b00 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 9 Dec 2021 12:13:00 +0100 Subject: [PATCH 173/492] removed unused imports --- openpype/settings/entities/op_version_entity.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/openpype/settings/entities/op_version_entity.py b/openpype/settings/entities/op_version_entity.py index cf2150f12ec..97b3efa028f 100644 --- a/openpype/settings/entities/op_version_entity.py +++ b/openpype/settings/entities/op_version_entity.py @@ -1,8 +1,5 @@ from openpype.lib.openpype_version import ( - op_version_control_available, get_remote_versions, - openpype_path_is_set, - openpype_path_is_accessible, get_OpenPypeVersion, get_build_version ) From 070078d1578cb3bb177cbb53a807bd145602d084 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 9 Dec 2021 12:13:12 +0100 Subject: [PATCH 174/492] do not add build version to staging --- openpype/settings/entities/op_version_entity.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/settings/entities/op_version_entity.py b/openpype/settings/entities/op_version_entity.py index 97b3efa028f..bd481dc497e 100644 --- a/openpype/settings/entities/op_version_entity.py +++ b/openpype/settings/entities/op_version_entity.py @@ -65,5 +65,4 @@ class StagingVersionsInputEntity(OpenPypeVersionInput): def _get_openpype_versions(self): versions = get_remote_versions(staging=True, production=False) - versions.append(get_build_version()) return sorted(versions) From 43c552dbbfd74712eeebb57e22bc1ef4b807b92e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 9 Dec 2021 12:13:36 +0100 Subject: [PATCH 175/492] added option to pass global settings to 'get_expected_studio_version_str' --- igniter/bootstrap_repos.py | 7 +++++-- igniter/tools.py | 8 ++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/igniter/bootstrap_repos.py b/igniter/bootstrap_repos.py index 77fcd15f1e5..70e6a75b9d4 100644 --- a/igniter/bootstrap_repos.py +++ b/igniter/bootstrap_repos.py @@ -591,19 +591,22 @@ def get_latest_version( return all_versions[-1] @classmethod - def get_expected_studio_version(cls, staging=False): + def get_expected_studio_version(cls, staging=False, global_settings=None): """Expected OpenPype version that should be used at the moment. If version is not defined in settings the latest found version is used. + Using precached global settings is needed for usage inside OpenPype. + Args: staging (bool): Staging version or production version. + global_settings (dict): Optional precached global settings. Returns: OpenPypeVersion: Version that should be used. """ - result = get_expected_studio_version_str(staging) + result = get_expected_studio_version_str(staging, global_settings) if not result: return None return OpenPypeVersion(version=result) diff --git a/igniter/tools.py b/igniter/tools.py index 2595140582a..735402e9a2d 100644 --- a/igniter/tools.py +++ b/igniter/tools.py @@ -187,17 +187,21 @@ def get_openpype_path_from_db(url: str) -> Union[str, None]: return None -def get_expected_studio_version_str(staging=False) -> str: +def get_expected_studio_version_str( + staging=False, global_settings=None +) -> str: """Version that should be currently used in studio. Args: staging (bool): Get current version for staging. + global_settings (dict): Optional precached global settings. Returns: str: OpenPype version which should be used. Empty string means latest. """ mongo_url = os.environ.get("OPENPYPE_MONGO") - global_settings = get_openpype_global_settings(mongo_url) + if global_settings is None: + global_settings = get_openpype_global_settings(mongo_url) if staging: key = "staging_version" else: From ad99d2a841f4cf581f13f2ef1e9f93e95c84760e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 9 Dec 2021 12:21:12 +0100 Subject: [PATCH 176/492] added few docstrings --- igniter/__init__.py | 4 ++++ igniter/message_dialog.py | 2 +- .../settings/entities/op_version_entity.py | 19 ++++++++++++++++++- openpype/tools/settings/settings/base.py | 1 + 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/igniter/__init__.py b/igniter/__init__.py index 6f06c1eb90e..02cba6a483a 100644 --- a/igniter/__init__.py +++ b/igniter/__init__.py @@ -12,6 +12,9 @@ ) from .version import __version__ as version +# Store OpenPypeVersion to 'sys.modules' +# - this makes it available in OpenPype processes without modifying +# 'sys.path' or 'PYTHONPATH' if "OpenPypeVersion" not in sys.modules: sys.modules["OpenPypeVersion"] = OpenPypeVersion @@ -64,6 +67,7 @@ def open_update_window(openpype_version): def show_message_dialog(title, message): + """Show dialog with a message and title to user.""" if os.getenv("OPENPYPE_HEADLESS_MODE"): print("!!! Can't open dialog in headless mode. Exiting.") sys.exit(1) diff --git a/igniter/message_dialog.py b/igniter/message_dialog.py index 88a086df1e8..c8e875cc37e 100644 --- a/igniter/message_dialog.py +++ b/igniter/message_dialog.py @@ -7,6 +7,7 @@ class MessageDialog(QtWidgets.QDialog): + """Simple message dialog with title, message and OK button.""" def __init__(self, title, message): super(MessageDialog, self).__init__() @@ -27,7 +28,6 @@ def __init__(self, title, message): btns_layout.addWidget(ok_btn, 0) layout = QtWidgets.QVBoxLayout(self) - # layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(label_widget, 1) layout.addLayout(btns_layout, 0) diff --git a/openpype/settings/entities/op_version_entity.py b/openpype/settings/entities/op_version_entity.py index bd481dc497e..62c5bf4ff90 100644 --- a/openpype/settings/entities/op_version_entity.py +++ b/openpype/settings/entities/op_version_entity.py @@ -12,6 +12,16 @@ class OpenPypeVersionInput(TextEntity): + """Entity to store OpenPype version to use. + + It is text input as creating of settings on different machines may + affect which versions are available so it must have option to set OpenPype + version which is not available for machine where settings entities are + loaded. + + It is possible to enter empty string. In that case is used any latest + version. Any other string must match regex of OpenPype version semantic. + """ def _item_initialization(self): super(OpenPypeVersionInput, self)._item_initialization() self.multiline = False @@ -19,9 +29,13 @@ def _item_initialization(self): self.value_hints = [] def _get_openpype_versions(self): - return [] + """This is abstract method returning version hints for UI purposes.""" + raise NotImplementedError(( + "{} does not have implemented '_get_openpype_versions'" + ).format(self.__class__.__name__)) def set_override_state(self, state, *args, **kwargs): + """Update value hints for UI purposes.""" value_hints = [] if state is OverrideState.STUDIO: versions = self._get_openpype_versions() @@ -36,6 +50,7 @@ def set_override_state(self, state, *args, **kwargs): ) def convert_to_valid_type(self, value): + """Add validation of version regex.""" if value and value is not NOT_SET: OpenPypeVersion = get_OpenPypeVersion() if OpenPypeVersion is not None: @@ -52,6 +67,7 @@ def convert_to_valid_type(self, value): class ProductionVersionsInputEntity(OpenPypeVersionInput): + """Entity meant only for global settings to define production version.""" schema_types = ["production-versions-text"] def _get_openpype_versions(self): @@ -61,6 +77,7 @@ def _get_openpype_versions(self): class StagingVersionsInputEntity(OpenPypeVersionInput): + """Entity meant only for global settings to define staging version.""" schema_types = ["staging-versions-text"] def _get_openpype_versions(self): diff --git a/openpype/tools/settings/settings/base.py b/openpype/tools/settings/settings/base.py index bc6c6d10ff2..48c2b42ebdc 100644 --- a/openpype/tools/settings/settings/base.py +++ b/openpype/tools/settings/settings/base.py @@ -28,6 +28,7 @@ def __init__(self, category_widget, entity, entity_widget): @staticmethod def set_style_property(obj, property_name, property_value): + """Change QWidget property and polish it's style.""" if obj.property(property_name) == property_value: return From 2673b587731576c30b26f6ec045c3cbff91f0fa3 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 9 Dec 2021 14:01:28 +0100 Subject: [PATCH 177/492] OP-2053 - added possibility to check installed extension version --- .../plugins/publish/collect_current_file.py | 2 +- .../publish/collect_extension_version.py | 56 +++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 openpype/hosts/aftereffects/plugins/publish/collect_extension_version.py diff --git a/openpype/hosts/aftereffects/plugins/publish/collect_current_file.py b/openpype/hosts/aftereffects/plugins/publish/collect_current_file.py index b59ff41a0eb..51f6f5c8448 100644 --- a/openpype/hosts/aftereffects/plugins/publish/collect_current_file.py +++ b/openpype/hosts/aftereffects/plugins/publish/collect_current_file.py @@ -8,7 +8,7 @@ class CollectCurrentFile(pyblish.api.ContextPlugin): """Inject the current working file into context""" - order = pyblish.api.CollectorOrder - 0.5 + order = pyblish.api.CollectorOrder - 0.49 label = "Current File" hosts = ["aftereffects"] diff --git a/openpype/hosts/aftereffects/plugins/publish/collect_extension_version.py b/openpype/hosts/aftereffects/plugins/publish/collect_extension_version.py new file mode 100644 index 00000000000..3352fd21f0e --- /dev/null +++ b/openpype/hosts/aftereffects/plugins/publish/collect_extension_version.py @@ -0,0 +1,56 @@ +import os +import re +import pyblish.api + +from avalon import aftereffects + + +class CollectExtensionVersion(pyblish.api.ContextPlugin): + """ Pulls and compares version of installed extension. + + It is recommended to use same extension as in provided Openpype code. + + Please use Anastasiy’s Extension Manager or ZXPInstaller to update + extension in case of an error. + + You can locate extension.zxp in your installed Openpype code in + `repos/avalon-core/avalon/aftereffects` + """ + # This technically should be a validator, but other collectors might be + # impacted with usage of obsolete extension, so collector that runs first + # was chosen + order = pyblish.api.CollectorOrder - 0.5 + label = "Collect extension version" + hosts = ["aftereffects"] + + optional = True + active = True + + def process(self, context): + installed_version = aftereffects.stub().get_extension_version() + + if not installed_version: + raise ValueError("Unknown version, probably old extension") + + manifest_url = os.path.join(os.path.dirname(aftereffects.__file__), + "extension", "CSXS", "manifest.xml") + + if not os.path.exists(manifest_url): + self.log.debug("Unable to locate extension manifest, not checking") + return + + expected_version = None + with open(manifest_url) as fp: + content = fp.read() + found = re.findall(r'(ExtensionBundleVersion=")([0-9\.]+)(")', + content) + if found: + expected_version = found[0][1] + + if expected_version != installed_version: + msg = "Expected version '{}' found '{}'\n".format( + expected_version, installed_version) + msg += "Please update your installed extension, it might not work " + msg += "properly." + + raise ValueError(msg) From a80ed0deecc0440fcbc779d4c855f5c6713db958 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 9 Dec 2021 14:25:24 +0100 Subject: [PATCH 178/492] OP-2053 - Hound --- openpype/hosts/aftereffects/plugins/publish/closeAE.py | 2 -- .../hosts/aftereffects/test_publish_in_aftereffects.py | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/openpype/hosts/aftereffects/plugins/publish/closeAE.py b/openpype/hosts/aftereffects/plugins/publish/closeAE.py index e6e96234743..21bedf0125d 100644 --- a/openpype/hosts/aftereffects/plugins/publish/closeAE.py +++ b/openpype/hosts/aftereffects/plugins/publish/closeAE.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- """Close AE after publish. For Webpublishing only.""" -import os - import pyblish.api from avalon import aftereffects diff --git a/tests/integration/hosts/aftereffects/test_publish_in_aftereffects.py b/tests/integration/hosts/aftereffects/test_publish_in_aftereffects.py index 3d1fa8f804d..e0f6b3e48e0 100644 --- a/tests/integration/hosts/aftereffects/test_publish_in_aftereffects.py +++ b/tests/integration/hosts/aftereffects/test_publish_in_aftereffects.py @@ -92,7 +92,7 @@ def test_db_asserts(self, dbcon, publish_finished): "Not expected no of representations" assert 1 == dbcon.count_documents({"type": "representation", - "context.subset": "imageMainBackgroundcopy", #noqa E501 + "context.subset": "imageMainBackgroundcopy", # noqa E501 "context.ext": "png"}), \ "Not expected no of representations with ext 'png'" From 2f2116b4bda283fa9b8e20464a97fb8a3dd25fc7 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 9 Dec 2021 18:30:43 +0100 Subject: [PATCH 179/492] OP-2042 - added test_data_folder to command line --- openpype/cli.py | 8 ++++++-- openpype/pype_commands.py | 18 +++++++++--------- tests/integration/conftest.py | 12 ++++++++++++ tests/lib/testing_classes.py | 8 ++++---- 4 files changed, 31 insertions(+), 15 deletions(-) create mode 100644 tests/integration/conftest.py diff --git a/openpype/cli.py b/openpype/cli.py index 4c4dc1a3c66..1f444006cab 100644 --- a/openpype/cli.py +++ b/openpype/cli.py @@ -356,9 +356,13 @@ def run(script): "--pyargs", help="Run tests from package", default=None) -def runtests(folder, mark, pyargs): +@click.option("-t", + "--test_data_folder", + help="Unzipped directory path of test file", + default=None) +def runtests(folder, mark, pyargs, test_data_folder): """Run all automatic tests after proper initialization via start.py""" - PypeCommands().run_tests(folder, mark, pyargs) + PypeCommands().run_tests(folder, mark, pyargs, test_data_folder) @main.command() diff --git a/openpype/pype_commands.py b/openpype/pype_commands.py index f01c6f0e42f..7b3c799b3ce 100644 --- a/openpype/pype_commands.py +++ b/openpype/pype_commands.py @@ -341,7 +341,7 @@ def run_application(self, app, project, asset, task, tools, arguments): def validate_jsons(self): pass - def run_tests(self, folder, mark, pyargs): + def run_tests(self, folder, mark, pyargs, test_data_folder): """ Runs tests from 'folder' @@ -349,26 +349,26 @@ def run_tests(self, folder, mark, pyargs): folder (str): relative path to folder with tests mark (str): label to run tests marked by it (slow etc) pyargs (str): package path to test + test_data_folder (str): url to unzipped folder of test data """ print("run_tests") - import subprocess - if folder: folder = " ".join(list(folder)) else: folder = "../tests" - mark_str = pyargs_str = '' + # disable warnings and show captured stdout even if success + args = ["--disable-pytest-warnings", "-rP", folder] + if mark: - mark_str = "-m {}".format(mark) + args.extend(["-m", mark]) if pyargs: - pyargs_str = "--pyargs {}".format(pyargs) + args.extend(["--pyargs", pyargs]) - # disable warnings and show captured stdout even if success - args = ["--disable-pytest-warnings", "-rP"] + if test_data_folder: + args.extend(["--test_data_folder", test_data_folder]) - args += [folder, mark_str, pyargs_str] print("run_tests args: {}".format(args)) import pytest pytest.main(args) diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py new file mode 100644 index 00000000000..bc002e8f86b --- /dev/null +++ b/tests/integration/conftest.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +import pytest + +def pytest_addoption(parser): + parser.addoption( + "--test_data_folder", action="store", default=None, + help="Provide url of a folder of unzipped test file" + ) + +@pytest.fixture(scope="module") +def test_data_folder(request): + return request.config.getoption("--test_data_folder") \ No newline at end of file diff --git a/tests/lib/testing_classes.py b/tests/lib/testing_classes.py index 25ad66cdfbf..541a92d15dd 100644 --- a/tests/lib/testing_classes.py +++ b/tests/lib/testing_classes.py @@ -58,10 +58,10 @@ def monkeypatch_session(self): m.undo() @pytest.fixture(scope="module") - def download_test_data(self): - if self.TEST_DATA_FOLDER: - print("Using existing folder {}".format(self.TEST_DATA_FOLDER)) - yield self.TEST_DATA_FOLDER + def download_test_data(self, test_data_folder): + if test_data_folder: + print("Using existing folder {}".format(test_data_folder)) + yield test_data_folder else: tmpdir = tempfile.mkdtemp() for test_file in self.TEST_FILES: From 53bbae2cbd1d19b25b6acbde3a0195aec58bfd2a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 9 Dec 2021 18:34:40 +0100 Subject: [PATCH 180/492] OP-2042 - better error message in validator --- .../hosts/nuke/plugins/publish/validate_rendered_frames.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/nuke/plugins/publish/validate_rendered_frames.py b/openpype/hosts/nuke/plugins/publish/validate_rendered_frames.py index 29faf867d27..af5e8e9d279 100644 --- a/openpype/hosts/nuke/plugins/publish/validate_rendered_frames.py +++ b/openpype/hosts/nuke/plugins/publish/validate_rendered_frames.py @@ -67,7 +67,9 @@ def process(self, instance): if not repre.get("files"): msg = ("no frames were collected, " - "you need to render them") + "you need to render them.\n" + "Check properties of write node (group) and" + "select 'Local' option in 'Publish' dropdown.") self.log.error(msg) raise ValidationException(msg) From 6b5706ef78a3c3bde0a4efd2235bd46dfe8b9d81 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 10 Dec 2021 10:38:23 +0100 Subject: [PATCH 181/492] added method to get installed version --- igniter/bootstrap_repos.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/igniter/bootstrap_repos.py b/igniter/bootstrap_repos.py index 70e6a75b9d4..b603fe74f1e 100644 --- a/igniter/bootstrap_repos.py +++ b/igniter/bootstrap_repos.py @@ -522,6 +522,16 @@ def get_versions_from_directory(openpype_dir: Path) -> List: return sorted(_openpype_versions) + @staticmethod + def get_installed_version_str() -> str: + """Get version of local OpenPype.""" + + version = {} + path = Path(os.environ["OPENPYPE_ROOT"]) / "openpype" / "version.py" + with open(path, "r") as fp: + exec(fp.read(), version) + return version["__version__"] + @classmethod def get_build_version(cls): """Get version of OpenPype inside build.""" From 1b4e5dd4435ae7acba7be6f21f6304a99ed9e938 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 10 Dec 2021 10:40:20 +0100 Subject: [PATCH 182/492] replaced get_local_live_version with get_installed_version_str --- igniter/bootstrap_repos.py | 12 +----------- start.py | 6 +++--- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/igniter/bootstrap_repos.py b/igniter/bootstrap_repos.py index b603fe74f1e..890df453a13 100644 --- a/igniter/bootstrap_repos.py +++ b/igniter/bootstrap_repos.py @@ -690,16 +690,6 @@ def get_version_path_from_list( return v.path return None - @staticmethod - def get_local_live_version() -> str: - """Get version of local OpenPype.""" - - version = {} - path = Path(os.environ["OPENPYPE_ROOT"]) / "openpype" / "version.py" - with open(path, "r") as fp: - exec(fp.read(), version) - return version["__version__"] - @staticmethod def get_version(repo_dir: Path) -> Union[str, None]: """Get version of OpenPype in given directory. @@ -747,7 +737,7 @@ def create_version_from_live_code( # version and use it as a source. Otherwise repo_dir is user # entered location. if not repo_dir: - version = self.get_local_live_version() + version = OpenPypeVersion.get_installed_version_str() repo_dir = self.live_repo_dir else: version = self.get_version(repo_dir) diff --git a/start.py b/start.py index 58c88df340e..975249b4e2b 100644 --- a/start.py +++ b/start.py @@ -794,7 +794,7 @@ def _bootstrap_from_code(use_version, use_staging): assert local_version else: # get current version of OpenPype - local_version = bootstrap.get_local_live_version() + local_version = OpenPypeVersion.get_installed_version_str() # All cases when should be used different version than build if (use_version and use_version != local_version) or use_staging: @@ -930,7 +930,7 @@ def boot(): if getattr(sys, 'frozen', False): local_version = bootstrap.get_version(Path(OPENPYPE_ROOT)) else: - local_version = bootstrap.get_local_live_version() + local_version = OpenPypeVersion.get_installed_version_str() if "validate" in commands: _print(f">>> Validating version [ {use_version} ]") @@ -978,7 +978,7 @@ def boot(): if getattr(sys, 'frozen', False): local_version = bootstrap.get_version(Path(_openpype_root)) else: - local_version = bootstrap.get_local_live_version() + local_version = OpenPypeVersion.get_installed_version_str() list_versions(openpype_versions, local_version) sys.exit(1) From 53e362f3db616ee95585856780b557cdcc502a25 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 10 Dec 2021 10:40:42 +0100 Subject: [PATCH 183/492] renamed build verion to installed version --- igniter/bootstrap_repos.py | 35 +++++++++++++++++------------------ start.py | 4 ++-- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/igniter/bootstrap_repos.py b/igniter/bootstrap_repos.py index 890df453a13..db62cbbe914 100644 --- a/igniter/bootstrap_repos.py +++ b/igniter/bootstrap_repos.py @@ -63,7 +63,7 @@ class OpenPypeVersion(semver.VersionInfo): staging = False path = None _VERSION_REGEX = re.compile(r"(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)(?:-(?P(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$") # noqa: E501 - _build_version = None + _installed_version = None def __init__(self, *args, **kwargs): """Create OpenPype version. @@ -533,17 +533,16 @@ def get_installed_version_str() -> str: return version["__version__"] @classmethod - def get_build_version(cls): + def get_installed_version(cls): """Get version of OpenPype inside build.""" - if cls._build_version is None: - openpype_root = Path(os.environ["OPENPYPE_ROOT"]) - build_version_str = BootstrapRepos.get_version(openpype_root) - if build_version_str: - cls._build_version = OpenPypeVersion( - version=build_version_str, - path=openpype_root + if cls._installed_version is None: + installed_version_str = cls.get_installed_version_str() + if installed_version_str: + cls._installed_version = OpenPypeVersion( + version=installed_version_str, + path=Path(os.environ["OPENPYPE_ROOT"]) ) - return cls._build_version + return cls._installed_version @staticmethod def get_latest_version( @@ -579,7 +578,7 @@ def get_latest_version( elif remote is None and not local: remote = True - build_version = OpenPypeVersion.get_build_version() + installed_version = OpenPypeVersion.get_installed_version() local_versions = [] remote_versions = [] if local: @@ -592,7 +591,7 @@ def get_latest_version( ) all_versions = local_versions + remote_versions if not staging: - all_versions.append(build_version) + all_versions.append(installed_version) if not all_versions: return None @@ -1118,9 +1117,9 @@ def find_openpype_version(version, staging): if isinstance(version, str): version = OpenPypeVersion(version=version) - build_version = OpenPypeVersion.get_build_version() - if build_version == version: - return build_version + installed_version = OpenPypeVersion.get_installed_version() + if installed_version == version: + return installed_version local_versions = OpenPypeVersion.get_local_versions( staging=staging, production=not staging @@ -1146,7 +1145,7 @@ def find_openpype_version(version, staging): @staticmethod def find_latest_openpype_version(staging): - build_version = OpenPypeVersion.get_build_version() + installed_version = OpenPypeVersion.get_installed_version() local_versions = OpenPypeVersion.get_local_versions( staging=staging ) @@ -1155,14 +1154,14 @@ def find_latest_openpype_version(staging): ) all_versions = local_versions + remote_versions if not staging: - all_versions.append(build_version) + all_versions.append(installed_version) if not all_versions: return None all_versions.sort() latest_version = all_versions[-1] - if latest_version == build_version: + if latest_version == installed_version: return latest_version if not latest_version.path.is_dir(): diff --git a/start.py b/start.py index 975249b4e2b..5a5039cd5cd 100644 --- a/start.py +++ b/start.py @@ -665,7 +665,7 @@ def _find_frozen_openpype(use_version: str = None, """ # Collect OpenPype versions - build_version = OpenPypeVersion.get_build_version() + installed_version = OpenPypeVersion.get_installed_version() # Expected version that should be used by studio settings # - this option is used only if version is not explictly set and if # studio has set explicit version in settings @@ -719,7 +719,7 @@ def _find_frozen_openpype(use_version: str = None, # get local frozen version and add it to detected version so if it is # newer it will be used instead. - if build_version == openpype_version: + if installed_version == openpype_version: version_path = _bootstrap_from_code(use_version, use_staging) openpype_version = OpenPypeVersion( version=BootstrapRepos.get_version(version_path), From 06a8f5014023ec1c792263fc66eb82c15159f908 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 10 Dec 2021 13:46:08 +0100 Subject: [PATCH 184/492] OP-2042 - evaluate paths in write nodes Paths in write nodes could contain python code for automatic testing. It needs to be evaluated to all os operations to work properly. --- .../nuke/plugins/publish/extract_render_local.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/nuke/plugins/publish/extract_render_local.py b/openpype/hosts/nuke/plugins/publish/extract_render_local.py index bc7b41c7332..50a5d014831 100644 --- a/openpype/hosts/nuke/plugins/publish/extract_render_local.py +++ b/openpype/hosts/nuke/plugins/publish/extract_render_local.py @@ -42,10 +42,14 @@ def process(self, instance): self.log.info("Start frame: {}".format(first_frame)) self.log.info("End frame: {}".format(last_frame)) + # write node url might contain nuke's ctl expressin + # as [python ...]/path... + path = node["file"].evaluate() + # Ensure output directory exists. - directory = os.path.dirname(node["file"].value()) - if not os.path.exists(directory): - os.makedirs(directory) + out_dir = os.path.dirname(path) + if not os.path.exists(out_dir): + os.makedirs(out_dir) # Render frames nuke.execute( @@ -58,15 +62,12 @@ def process(self, instance): if "slate" in families: first_frame += 1 - path = node['file'].value() - out_dir = os.path.dirname(path) ext = node["file_type"].value() if "representations" not in instance.data: instance.data["representations"] = [] collected_frames = os.listdir(out_dir) - if len(collected_frames) == 1: repre = { 'name': ext, From 00d9681d56e381169e47f6561a065ca9d2e4ea88 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 10 Dec 2021 13:47:55 +0100 Subject: [PATCH 185/492] OP-2042 - better cleanup of test DBs before start of test --- tests/lib/db_handler.py | 14 +++++++++++--- tests/lib/testing_classes.py | 6 ++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/tests/lib/db_handler.py b/tests/lib/db_handler.py index 0aa4c69ca6d..b1810550124 100644 --- a/tests/lib/db_handler.py +++ b/tests/lib/db_handler.py @@ -112,9 +112,17 @@ def setup_from_dump(self, db_name, dump_dir, overwrite=False, source 'db_name' """ db_name_out = db_name_out or db_name - if self._db_exists(db_name) and not overwrite: - raise RuntimeError("DB {} already exists".format(db_name_out) + - "Run with overwrite=True") + if self._db_exists(db_name_out): + if not overwrite: + raise RuntimeError("DB {} already exists".format(db_name_out) + + "Run with overwrite=True") + else: + if collection: + coll = self.client[db_name_out].get(collection) + if coll: + coll.drop() + else: + self.teardown(db_name_out) dir_path = os.path.join(dump_dir, db_name) if not os.path.exists(dir_path): diff --git a/tests/lib/testing_classes.py b/tests/lib/testing_classes.py index 541a92d15dd..96e7148bffa 100644 --- a/tests/lib/testing_classes.py +++ b/tests/lib/testing_classes.py @@ -130,10 +130,12 @@ def db_setup(self, download_test_data, env_var, monkeypatch_session): uri = os.environ.get("OPENPYPE_MONGO") db_handler = DBHandler(uri) - db_handler.setup_from_dump(self.TEST_DB_NAME, backup_dir, True, + db_handler.setup_from_dump(self.TEST_DB_NAME, backup_dir, + overwrite=True, db_name_out=self.TEST_DB_NAME) - db_handler.setup_from_dump("openpype", backup_dir, True, + db_handler.setup_from_dump("openpype", backup_dir, + overwrite=True, db_name_out=self.TEST_OPENPYPE_NAME) yield db_handler From 6e9c9c087cd5047c5f4d7e32b6019ce6a782df3d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 10 Dec 2021 15:04:10 +0100 Subject: [PATCH 186/492] OP-2042 - adding persist, app_variant to cli --- openpype/cli.py | 13 +++++- openpype/pype_commands.py | 15 ++++++- tests/integration/conftest.py | 25 ++++++++++- .../hosts/nuke/test_publish_in_nuke.py | 15 ++++--- tests/lib/testing_classes.py | 45 ++++++++++++------- 5 files changed, 85 insertions(+), 28 deletions(-) diff --git a/openpype/cli.py b/openpype/cli.py index 1f444006cab..6b20fb52037 100644 --- a/openpype/cli.py +++ b/openpype/cli.py @@ -360,9 +360,18 @@ def run(script): "--test_data_folder", help="Unzipped directory path of test file", default=None) -def runtests(folder, mark, pyargs, test_data_folder): +@click.option("-s", + "--persist", + help="Persist test DB and published files after test end", + default=None) +@click.option("-a", + "--app_variant", + help="Provide specific app variant for test, empty for latest", + default=None) +def runtests(folder, mark, pyargs, test_data_folder, persist, app_variant): """Run all automatic tests after proper initialization via start.py""" - PypeCommands().run_tests(folder, mark, pyargs, test_data_folder) + PypeCommands().run_tests(folder, mark, pyargs, test_data_folder, + persist, app_variant) @main.command() diff --git a/openpype/pype_commands.py b/openpype/pype_commands.py index 7b3c799b3ce..a6330bae1fd 100644 --- a/openpype/pype_commands.py +++ b/openpype/pype_commands.py @@ -341,7 +341,8 @@ def run_application(self, app, project, asset, task, tools, arguments): def validate_jsons(self): pass - def run_tests(self, folder, mark, pyargs, test_data_folder): + def run_tests(self, folder, mark, pyargs, + test_data_folder, persist, app_variant): """ Runs tests from 'folder' @@ -350,6 +351,10 @@ def run_tests(self, folder, mark, pyargs, test_data_folder): mark (str): label to run tests marked by it (slow etc) pyargs (str): package path to test test_data_folder (str): url to unzipped folder of test data + persist (bool): True if keep test db and published after test + end + app_variant (str): variant (eg 2020 for AE), empty if use + latest installed version """ print("run_tests") if folder: @@ -366,9 +371,15 @@ def run_tests(self, folder, mark, pyargs, test_data_folder): if pyargs: args.extend(["--pyargs", pyargs]) - if test_data_folder: + if persist: args.extend(["--test_data_folder", test_data_folder]) + if persist: + args.extend(["--persist", persist]) + + if app_variant: + args.extend(["--app_variant", app_variant]) + print("run_tests args: {}".format(args)) import pytest pytest.main(args) diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index bc002e8f86b..400c0dcc2a9 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -1,12 +1,35 @@ # -*- coding: utf-8 -*- +# adds command line arguments for 'runtests' as a fixtures import pytest + def pytest_addoption(parser): parser.addoption( "--test_data_folder", action="store", default=None, help="Provide url of a folder of unzipped test file" ) + parser.addoption( + "--persist", action="store", default=None, + help="True - keep test_db, test_openpype, outputted test files" + ) + + parser.addoption( + "--app_variant", action="store", default=None, + help="Keep empty to locate latest installed variant or explicit" + ) + + @pytest.fixture(scope="module") def test_data_folder(request): - return request.config.getoption("--test_data_folder") \ No newline at end of file + return request.config.getoption("--test_data_folder") + + +@pytest.fixture(scope="module") +def persist(request): + return request.config.getoption("--persist") + + +@pytest.fixture(scope="module") +def app_variant(request): + return request.config.getoption("--app_variant") diff --git a/tests/integration/hosts/nuke/test_publish_in_nuke.py b/tests/integration/hosts/nuke/test_publish_in_nuke.py index 092fd7d1c6f..a5a09bdd04f 100644 --- a/tests/integration/hosts/nuke/test_publish_in_nuke.py +++ b/tests/integration/hosts/nuke/test_publish_in_nuke.py @@ -27,22 +27,23 @@ class TestPublishInNuke(PublishTest): To check log/errors from launched app's publish process keep PERSIST to True and check `test_openpype.logs` collection. """ - PERSIST = True # True - keep test_db, test_openpype, outputted test files - + # https://drive.google.com/file/d/1SUurHj2aiQ21ZIMJfGVBI2KjR8kIjBGI/view?usp=sharing # noqa: E501 TEST_FILES = [ ("1SUurHj2aiQ21ZIMJfGVBI2KjR8kIjBGI", "test_Nuke_publish.zip", "") ] APP = "nuke" - # keep empty to locate latest installed variant or explicit - APP_VARIANT = "" TIMEOUT = 120 # publish timeout - TEST_DATA_FOLDER = "C:\\Users\\petrk\\AppData\\Local\\Temp\\tmpbfh976y6" # provide existing folder with test data + # could be overwritten by command line arguments + # keep empty to locate latest installed variant or explicit + APP_VARIANT = "" + PERSIST = True # True - keep test_db, test_openpype, outputted test files + TEST_DATA_FOLDER = None @pytest.fixture(scope="module") - def last_workfile_path(self, download_test_data): + def last_workfile_path(self, download_test_data, output_folder_url): """Get last_workfile_path from source data. """ @@ -99,7 +100,7 @@ def test_db_asserts(self, dbcon, publish_finished): "name": "workfileTest_task"}), \ "workfileTest_task subset must be present" - assert 10 == dbcon.count_documents({"type": "representation"}), \ + assert 4 == dbcon.count_documents({"type": "representation"}), \ "Not expected no of representations" reprs = dbcon.count_documents({"type": "representation", diff --git a/tests/lib/testing_classes.py b/tests/lib/testing_classes.py index 96e7148bffa..40363e928f0 100644 --- a/tests/lib/testing_classes.py +++ b/tests/lib/testing_classes.py @@ -7,6 +7,7 @@ import tempfile import shutil import glob +import platform from tests.lib.db_handler import DBHandler from tests.lib.file_handler import RemoteFileHandler @@ -58,7 +59,8 @@ def monkeypatch_session(self): m.undo() @pytest.fixture(scope="module") - def download_test_data(self, test_data_folder): + def download_test_data(self, test_data_folder, persist=False): + test_data_folder = test_data_folder or self.TEST_DATA_FOLDER if test_data_folder: print("Using existing folder {}".format(test_data_folder)) yield test_data_folder @@ -78,7 +80,8 @@ def download_test_data(self, test_data_folder): print("Temporary folder created:: {}".format(tmpdir)) yield tmpdir - if not self.PERSIST: + persist = persist or self.PERSIST + if not persist: print("Removing {}".format(tmpdir)) shutil.rmtree(tmpdir) @@ -188,14 +191,28 @@ class PublishTest(ModuleUnitTest): """ APP = "" - APP_VARIANT = "" # keep empty to locate latest installed variant TIMEOUT = 120 # publish timeout - @property - def app_name(self): - if self.APP_VARIANT: - return "{}/{}".format(self.APP, self.APP_VARIANT) + # could be overwritten by command line arguments + # command line value takes precedence + + # keep empty to locate latest installed variant or explicit + APP_VARIANT = "" + PERSIST = True # True - keep test_db, test_openpype, outputted test files + TEST_DATA_FOLDER = None # use specific folder of unzipped test file + + @pytest.fixture(scope="module") + def app_name(self, app_variant): + """Returns calculated value for ApplicationManager. Eg.(nuke/12-2)""" + from openpype.lib import ApplicationManager + app_variant = app_variant or self.APP_VARIANT + + application_manager = ApplicationManager() + if not app_variant: + app_variant = find_variant_key(application_manager, self.APP) + + yield "{}/{}".format(self.APP, app_variant) @pytest.fixture(scope="module") def last_workfile_path(self, download_test_data): @@ -203,6 +220,7 @@ def last_workfile_path(self, download_test_data): @pytest.fixture(scope="module") def startup_scripts(self, monkeypatch_session, download_test_data): + """"Adds init scripts (like userSetup) to expected location""" raise NotImplementedError @pytest.fixture(scope="module") @@ -270,12 +288,6 @@ def launched_app(self, dbcon, download_test_data, last_workfile_path, if app_args: data["app_args"] = app_args - variant = self.APP_VARIANT - if not variant: - variant = find_variant_key(application_manager, self.APP) - - app_name = "{}/{}".format(self.APP, variant) - app_process = application_manager.launch(app_name, **data) yield app_process @@ -295,13 +307,13 @@ def publish_finished(self, dbcon, launched_app, download_test_data): yield True def test_folder_structure_same(self, dbcon, publish_finished, - download_test_data): + download_test_data, output_folder_url): """Check if expected and published subfolders contain same files. Compares only presence, not size nor content! """ published_dir_base = download_test_data - published_dir = os.path.join(published_dir_base, + published_dir = os.path.join(output_folder_url, self.PROJECT, self.TASK, "**") @@ -311,7 +323,8 @@ def test_folder_structure_same(self, dbcon, publish_finished, self.PROJECT, self.TASK, "**") - + print("Comparing published:'{}' : expected:'{}'".format(published_dir, + expected_dir)) published = set(f.replace(published_dir_base, '') for f in glob.glob(published_dir, recursive=True) if f != published_dir_base and os.path.exists(f)) From 9a0d55e2f3c6e07d1ccfffd3cd1f5f04669b15b7 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 10 Dec 2021 15:08:13 +0100 Subject: [PATCH 187/492] OP-2042 - added new fixture output_folder_url Explicitly sets directory of published files. (Purges them if exist!) --- .../hosts/maya/test_publish_in_maya.py | 4 ++-- .../hosts/nuke/test_publish_in_nuke.py | 2 +- .../photoshop/test_publish_in_photoshop.py | 4 ++-- tests/lib/testing_classes.py | 19 +++++++++++++++---- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/tests/integration/hosts/maya/test_publish_in_maya.py b/tests/integration/hosts/maya/test_publish_in_maya.py index 687e6fbc6ea..5f3a550c6ad 100644 --- a/tests/integration/hosts/maya/test_publish_in_maya.py +++ b/tests/integration/hosts/maya/test_publish_in_maya.py @@ -39,7 +39,7 @@ class TestPublishInMaya(PublishTest): TIMEOUT = 120 # publish timeout @pytest.fixture(scope="module") - def last_workfile_path(self, download_test_data): + def last_workfile_path(self, download_test_data, output_folder_url): """Get last_workfile_path from source data. Maya expects workfile in proper folder, so copy is done first. @@ -48,7 +48,7 @@ def last_workfile_path(self, download_test_data): "input", "workfile", "test_project_test_asset_TestTask_v001.mb") - dest_folder = os.path.join(download_test_data, + dest_folder = os.path.join(output_folder_url, self.PROJECT, self.ASSET, "work", diff --git a/tests/integration/hosts/nuke/test_publish_in_nuke.py b/tests/integration/hosts/nuke/test_publish_in_nuke.py index a5a09bdd04f..797bc0a9d3a 100644 --- a/tests/integration/hosts/nuke/test_publish_in_nuke.py +++ b/tests/integration/hosts/nuke/test_publish_in_nuke.py @@ -52,7 +52,7 @@ def last_workfile_path(self, download_test_data, output_folder_url): "input", "workfile", source_file_name) - dest_folder = os.path.join(download_test_data, + dest_folder = os.path.join(output_folder_url, self.PROJECT, self.ASSET, "work", diff --git a/tests/integration/hosts/photoshop/test_publish_in_photoshop.py b/tests/integration/hosts/photoshop/test_publish_in_photoshop.py index c7f23994949..541552fedf7 100644 --- a/tests/integration/hosts/photoshop/test_publish_in_photoshop.py +++ b/tests/integration/hosts/photoshop/test_publish_in_photoshop.py @@ -37,7 +37,7 @@ class TestPublishInPhotoshop(PublishTest): TIMEOUT = 120 # publish timeout @pytest.fixture(scope="module") - def last_workfile_path(self, download_test_data): + def last_workfile_path(self, download_test_data, output_folder_url): """Get last_workfile_path from source data. Maya expects workfile in proper folder, so copy is done first. @@ -46,7 +46,7 @@ def last_workfile_path(self, download_test_data): "input", "workfile", "test_project_test_asset_TestTask_v001.psd") - dest_folder = os.path.join(download_test_data, + dest_folder = os.path.join(output_folder_url, self.PROJECT, self.ASSET, "work", diff --git a/tests/lib/testing_classes.py b/tests/lib/testing_classes.py index 40363e928f0..ad637e69740 100644 --- a/tests/lib/testing_classes.py +++ b/tests/lib/testing_classes.py @@ -215,7 +215,17 @@ def app_name(self, app_variant): yield "{}/{}".format(self.APP, app_variant) @pytest.fixture(scope="module") - def last_workfile_path(self, download_test_data): + def output_folder_url(self, download_test_data): + """Returns location of published data, cleans it first if exists.""" + path = os.path.join(download_test_data, "output") + if os.path.exists(path): + print("Purging {}".format(path)) + shutil.rmtree(path) + yield path + + @pytest.fixture(scope="module") + def last_workfile_path(self, download_test_data, output_folder_url): + """Returns url of workfile""" raise NotImplementedError @pytest.fixture(scope="module") @@ -251,15 +261,16 @@ def app_args(self, download_test_data): @pytest.fixture(scope="module") def launched_app(self, dbcon, download_test_data, last_workfile_path, - startup_scripts, app_args): + startup_scripts, app_args, app_name, output_folder_url): """Launch host app""" # set publishing folders - root_key = "config.roots.work.{}".format("windows") # TEMP + platform_str = platform.system().lower() + root_key = "config.roots.work.{}".format(platform_str) dbcon.update_one( {"type": "project"}, {"$set": { - root_key: download_test_data + root_key: output_folder_url }} ) From 09bbebae18bca1da746229963bc45cf2f48f8d32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20LORRAIN?= Date: Fri, 10 Dec 2021 15:14:03 +0100 Subject: [PATCH 188/492] Add settings to repair --- .../publish/validate_render_image_rule.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_render_image_rule.py b/openpype/hosts/maya/plugins/publish/validate_render_image_rule.py index a912431b559..642ca9e25d0 100644 --- a/openpype/hosts/maya/plugins/publish/validate_render_image_rule.py +++ b/openpype/hosts/maya/plugins/publish/validate_render_image_rule.py @@ -23,11 +23,7 @@ class ValidateRenderImageRule(pyblish.api.InstancePlugin): def process(self, instance): - default_render_file = instance.context.data.get('project_settings')\ - .get('maya') \ - .get('create') \ - .get('CreateRender') \ - .get('default_render_image_folder') + default_render_file = self.get_default_render_image_folder(instance) assert get_file_rule("images") == default_render_file, ( "Workspace's `images` file rule must be set to: {}".format( @@ -37,5 +33,14 @@ def process(self, instance): @classmethod def repair(cls, instance): - pm.workspace.fileRules["images"] = "renders" + default = cls.get_default_render_image_folder(instance) + pm.workspace.fileRules["images"] = default pm.system.Workspace.save() + + @staticmethod + def get_default_render_image_folder(instance): + return instance.context.data.get('project_settings')\ + .get('maya') \ + .get('create') \ + .get('CreateRender') \ + .get('default_render_image_folder') From c754521d0aa5ba0bed5baab997e3278224d7a602 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 10 Dec 2021 16:40:23 +0100 Subject: [PATCH 189/492] OP-2042 - added new handling of asserts Added DBAssert class which wraps standard use cases for asserts --- .../hosts/nuke/test_publish_in_nuke.py | 53 +++++++++---------- tests/lib/assert_classes.py | 45 ++++++++++++++++ 2 files changed, 71 insertions(+), 27 deletions(-) create mode 100644 tests/lib/assert_classes.py diff --git a/tests/integration/hosts/nuke/test_publish_in_nuke.py b/tests/integration/hosts/nuke/test_publish_in_nuke.py index 797bc0a9d3a..6b1088206a2 100644 --- a/tests/integration/hosts/nuke/test_publish_in_nuke.py +++ b/tests/integration/hosts/nuke/test_publish_in_nuke.py @@ -4,6 +4,7 @@ import shutil from tests.lib.testing_classes import PublishTest +from tests.lib.assert_classes import DBAssert log = logging.getLogger("test_publish_in_nuke") @@ -82,33 +83,31 @@ def startup_scripts(self, monkeypatch_session, download_test_data): def test_db_asserts(self, dbcon, publish_finished): """Host and input data dependent expected results in DB.""" print("test_db_asserts") - versions = dbcon.count_documents({"type": "version"}) - assert 2 == versions, \ - "Not expected no of versions. "\ - "Expected 2, found {}".format(versions) - - assert 0 == dbcon.count_documents({"type": "version", - "name": {"$ne": 1}}), \ - "Only versions with 1 expected" - - assert 1 == dbcon.count_documents({"type": "subset", - "name": "renderCompositingInNukeMain"} # noqa: E501 - ), \ - "renderCompositingInNukeMain subset must be present" - - assert 1 == dbcon.count_documents({"type": "subset", - "name": "workfileTest_task"}), \ - "workfileTest_task subset must be present" - - assert 4 == dbcon.count_documents({"type": "representation"}), \ - "Not expected no of representations" - - reprs = dbcon.count_documents({"type": "representation", - "context.subset": "renderCompositingInNukeMain", # noqa: E501 - "context.ext": "exr"}) - assert 1 == reprs, \ - "Not expected no of representations with ext 'exr'."\ - "Expected 1, found {}".format(reprs) + failures = [] + + failures.append(DBAssert.count_of_types(dbcon, "version", 2)) + + failures.append( + DBAssert.count_of_types(dbcon, "version", 0, name={"$ne": 1})) + + failures.append( + DBAssert.count_of_types(dbcon, "subset", 1, + name="renderCompositingInNukeMain")) + + failures.append( + DBAssert.count_of_types(dbcon, "subset", 1, + name="workfileTest_task")) + + failures.append( + DBAssert.count_of_types(dbcon, "representation", 4)) + + additional_args = {"context.subset": "renderCompositingInNukeMain", + "context.ext": "exr"} + failures.append( + DBAssert.count_of_types(dbcon, "representation", 1, + additional_args=additional_args)) + + assert not any(failures) if __name__ == "__main__": diff --git a/tests/lib/assert_classes.py b/tests/lib/assert_classes.py new file mode 100644 index 00000000000..7298853b678 --- /dev/null +++ b/tests/lib/assert_classes.py @@ -0,0 +1,45 @@ +"""Classed and methods for comparing expected and published items in DBs""" + +class DBAssert: + + @classmethod + def count_of_types(cls, dbcon, queried_type, expected, **kwargs): + """Queries 'dbcon' and counts documents of type 'queried_type' + + Args: + dbcon (AvalonMongoDB) + queried_type (str): type of document ("asset", "version"...) + expected (int): number of documents found + any number of additional keyword arguments + + special handling of argument additional_args (dict) + with additional args like + {"context.subset": "XXX"} + """ + args = {"type": queried_type} + for key, val in kwargs.items(): + if key == "additional_args": + args.update(val) + else: + args[key] = val + + msg = None + no_of_docs = dbcon.count_documents(args) + if expected != no_of_docs: + msg = "Not expected no of versions. "\ + "Expected {}, found {}".format(expected, no_of_docs) + + args.pop("type") + detail_str = " " + if args: + detail_str = " with {}".format(args) + + status = "successful" + if msg: + status = "failed" + + print("Comparing count of {}{} {}".format(queried_type, + detail_str, + status)) + + return msg From ec15b482dbfd38a0a4c18831f38009c900156632 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 10 Dec 2021 17:25:37 +0100 Subject: [PATCH 190/492] OP-2042 - added additional class wrapper per host --- tests/integration/hosts/maya/lib.py | 41 +++++++++++++++++ .../hosts/maya/test_publish_in_maya.py | 42 +----------------- tests/integration/hosts/nuke/lib.py | 44 +++++++++++++++++++ .../hosts/nuke/test_publish_in_nuke.py | 44 +------------------ tests/integration/hosts/photoshop/lib.py | 34 ++++++++++++++ .../photoshop/test_publish_in_photoshop.py | 35 +-------------- tests/lib/testing_classes.py | 23 +++++----- 7 files changed, 138 insertions(+), 125 deletions(-) create mode 100644 tests/integration/hosts/maya/lib.py create mode 100644 tests/integration/hosts/nuke/lib.py create mode 100644 tests/integration/hosts/photoshop/lib.py diff --git a/tests/integration/hosts/maya/lib.py b/tests/integration/hosts/maya/lib.py new file mode 100644 index 00000000000..f3a438c0650 --- /dev/null +++ b/tests/integration/hosts/maya/lib.py @@ -0,0 +1,41 @@ +import os +import pytest +import shutil + +from tests.lib.testing_classes import HostFixtures + + +class MayaTestClass(HostFixtures): + @pytest.fixture(scope="module") + def last_workfile_path(self, download_test_data, output_folder_url): + """Get last_workfile_path from source data. + + Maya expects workfile in proper folder, so copy is done first. + """ + src_path = os.path.join(download_test_data, + "input", + "workfile", + "test_project_test_asset_TestTask_v001.mb") + dest_folder = os.path.join(output_folder_url, + self.PROJECT, + self.ASSET, + "work", + self.TASK) + os.makedirs(dest_folder) + dest_path = os.path.join(dest_folder, + "test_project_test_asset_TestTask_v001.mb") + shutil.copy(src_path, dest_path) + + yield dest_path + + @pytest.fixture(scope="module") + def startup_scripts(self, monkeypatch_session, download_test_data): + """Points Maya to userSetup file from input data""" + startup_path = os.path.join(download_test_data, + "input", + "startup") + original_pythonpath = os.environ.get("PYTHONPATH") + monkeypatch_session.setenv("PYTHONPATH", + "{}{}{}".format(startup_path, + os.pathsep, + original_pythonpath)) diff --git a/tests/integration/hosts/maya/test_publish_in_maya.py b/tests/integration/hosts/maya/test_publish_in_maya.py index 5f3a550c6ad..68b05644280 100644 --- a/tests/integration/hosts/maya/test_publish_in_maya.py +++ b/tests/integration/hosts/maya/test_publish_in_maya.py @@ -1,11 +1,7 @@ -import pytest -import os -import shutil +from tests.integration.hosts.maya.lib import MayaTestClass -from tests.lib.testing_classes import PublishTest - -class TestPublishInMaya(PublishTest): +class TestPublishInMaya(MayaTestClass): """Basic test case for publishing in Maya Shouldnt be running standalone only via 'runtests' pype command! (??) @@ -38,40 +34,6 @@ class TestPublishInMaya(PublishTest): TIMEOUT = 120 # publish timeout - @pytest.fixture(scope="module") - def last_workfile_path(self, download_test_data, output_folder_url): - """Get last_workfile_path from source data. - - Maya expects workfile in proper folder, so copy is done first. - """ - src_path = os.path.join(download_test_data, - "input", - "workfile", - "test_project_test_asset_TestTask_v001.mb") - dest_folder = os.path.join(output_folder_url, - self.PROJECT, - self.ASSET, - "work", - self.TASK) - os.makedirs(dest_folder) - dest_path = os.path.join(dest_folder, - "test_project_test_asset_TestTask_v001.mb") - shutil.copy(src_path, dest_path) - - yield dest_path - - @pytest.fixture(scope="module") - def startup_scripts(self, monkeypatch_session, download_test_data): - """Points Maya to userSetup file from input data""" - startup_path = os.path.join(download_test_data, - "input", - "startup") - original_pythonpath = os.environ.get("PYTHONPATH") - monkeypatch_session.setenv("PYTHONPATH", - "{}{}{}".format(startup_path, - os.pathsep, - original_pythonpath)) - def test_db_asserts(self, dbcon, publish_finished): """Host and input data dependent expected results in DB.""" print("test_db_asserts") diff --git a/tests/integration/hosts/nuke/lib.py b/tests/integration/hosts/nuke/lib.py new file mode 100644 index 00000000000..d3c3d7ba816 --- /dev/null +++ b/tests/integration/hosts/nuke/lib.py @@ -0,0 +1,44 @@ +import os +import pytest +import shutil + +from tests.lib.testing_classes import HostFixtures + + +class NukeTestClass(HostFixtures): + @pytest.fixture(scope="module") + def last_workfile_path(self, download_test_data, output_folder_url): + """Get last_workfile_path from source data. + + """ + source_file_name = "test_project_test_asset_CompositingInNuke_v001.nk" + src_path = os.path.join(download_test_data, + "input", + "workfile", + source_file_name) + dest_folder = os.path.join(output_folder_url, + self.PROJECT, + self.ASSET, + "work", + self.TASK) + if not os.path.exists(dest_folder): + os.makedirs(dest_folder) + + dest_path = os.path.join(dest_folder, + source_file_name) + + shutil.copy(src_path, dest_path) + + yield dest_path + + @pytest.fixture(scope="module") + def startup_scripts(self, monkeypatch_session, download_test_data): + """Points Nuke to userSetup file from input data""" + startup_path = os.path.join(download_test_data, + "input", + "startup") + original_nuke_path = os.environ.get("NUKE_PATH", "") + monkeypatch_session.setenv("NUKE_PATH", + "{}{}{}".format(startup_path, + os.pathsep, + original_nuke_path)) \ No newline at end of file diff --git a/tests/integration/hosts/nuke/test_publish_in_nuke.py b/tests/integration/hosts/nuke/test_publish_in_nuke.py index 6b1088206a2..884160e0b59 100644 --- a/tests/integration/hosts/nuke/test_publish_in_nuke.py +++ b/tests/integration/hosts/nuke/test_publish_in_nuke.py @@ -1,15 +1,12 @@ -import pytest -import os import logging -import shutil -from tests.lib.testing_classes import PublishTest from tests.lib.assert_classes import DBAssert +from tests.integration.hosts.nuke.lib import NukeTestClass log = logging.getLogger("test_publish_in_nuke") -class TestPublishInNuke(PublishTest): +class TestPublishInNuke(NukeTestClass): """Basic test case for publishing in Nuke Uses generic TestCase to prepare fixtures for test data, testing DBs, @@ -43,43 +40,6 @@ class TestPublishInNuke(PublishTest): PERSIST = True # True - keep test_db, test_openpype, outputted test files TEST_DATA_FOLDER = None - @pytest.fixture(scope="module") - def last_workfile_path(self, download_test_data, output_folder_url): - """Get last_workfile_path from source data. - - """ - source_file_name = "test_project_test_asset_CompositingInNuke_v001.nk" - src_path = os.path.join(download_test_data, - "input", - "workfile", - source_file_name) - dest_folder = os.path.join(output_folder_url, - self.PROJECT, - self.ASSET, - "work", - self.TASK) - if not os.path.exists(dest_folder): - os.makedirs(dest_folder) - - dest_path = os.path.join(dest_folder, - source_file_name) - - shutil.copy(src_path, dest_path) - - yield dest_path - - @pytest.fixture(scope="module") - def startup_scripts(self, monkeypatch_session, download_test_data): - """Points Nuke to userSetup file from input data""" - startup_path = os.path.join(download_test_data, - "input", - "startup") - original_nuke_path = os.environ.get("NUKE_PATH", "") - monkeypatch_session.setenv("NUKE_PATH", - "{}{}{}".format(startup_path, - os.pathsep, - original_nuke_path)) - def test_db_asserts(self, dbcon, publish_finished): """Host and input data dependent expected results in DB.""" print("test_db_asserts") diff --git a/tests/integration/hosts/photoshop/lib.py b/tests/integration/hosts/photoshop/lib.py new file mode 100644 index 00000000000..16ef2d3ae68 --- /dev/null +++ b/tests/integration/hosts/photoshop/lib.py @@ -0,0 +1,34 @@ +import os +import pytest +import shutil + +from tests.lib.testing_classes import HostFixtures + + +class PhotoshopTestClass(HostFixtures): + @pytest.fixture(scope="module") + def last_workfile_path(self, download_test_data, output_folder_url): + """Get last_workfile_path from source data. + + Maya expects workfile in proper folder, so copy is done first. + """ + src_path = os.path.join(download_test_data, + "input", + "workfile", + "test_project_test_asset_TestTask_v001.psd") + dest_folder = os.path.join(output_folder_url, + self.PROJECT, + self.ASSET, + "work", + self.TASK) + os.makedirs(dest_folder) + dest_path = os.path.join(dest_folder, + "test_project_test_asset_TestTask_v001.psd") + shutil.copy(src_path, dest_path) + + yield dest_path + + @pytest.fixture(scope="module") + def startup_scripts(self, monkeypatch_session, download_test_data): + """Points Maya to userSetup file from input data""" + pass diff --git a/tests/integration/hosts/photoshop/test_publish_in_photoshop.py b/tests/integration/hosts/photoshop/test_publish_in_photoshop.py index 541552fedf7..ab07577b4a3 100644 --- a/tests/integration/hosts/photoshop/test_publish_in_photoshop.py +++ b/tests/integration/hosts/photoshop/test_publish_in_photoshop.py @@ -1,11 +1,7 @@ -import pytest -import os -import shutil +from tests.integration.hosts.photoshop.lib import PhotoshopTestClass -from tests.lib.testing_classes import PublishTest - -class TestPublishInPhotoshop(PublishTest): +class TestPublishInPhotoshop(PhotoshopTestClass): """Basic test case for publishing in Photoshop Uses generic TestCase to prepare fixtures for test data, testing DBs, @@ -36,33 +32,6 @@ class TestPublishInPhotoshop(PublishTest): TIMEOUT = 120 # publish timeout - @pytest.fixture(scope="module") - def last_workfile_path(self, download_test_data, output_folder_url): - """Get last_workfile_path from source data. - - Maya expects workfile in proper folder, so copy is done first. - """ - src_path = os.path.join(download_test_data, - "input", - "workfile", - "test_project_test_asset_TestTask_v001.psd") - dest_folder = os.path.join(output_folder_url, - self.PROJECT, - self.ASSET, - "work", - self.TASK) - os.makedirs(dest_folder) - dest_path = os.path.join(dest_folder, - "test_project_test_asset_TestTask_v001.psd") - shutil.copy(src_path, dest_path) - - yield dest_path - - @pytest.fixture(scope="module") - def startup_scripts(self, monkeypatch_session, download_test_data): - """Points Maya to userSetup file from input data""" - pass - def test_db_asserts(self, dbcon, publish_finished): """Host and input data dependent expected results in DB.""" print("test_db_asserts") diff --git a/tests/lib/testing_classes.py b/tests/lib/testing_classes.py index ad637e69740..06922abc013 100644 --- a/tests/lib/testing_classes.py +++ b/tests/lib/testing_classes.py @@ -223,16 +223,6 @@ def output_folder_url(self, download_test_data): shutil.rmtree(path) yield path - @pytest.fixture(scope="module") - def last_workfile_path(self, download_test_data, output_folder_url): - """Returns url of workfile""" - raise NotImplementedError - - @pytest.fixture(scope="module") - def startup_scripts(self, monkeypatch_session, download_test_data): - """"Adds init scripts (like userSetup) to expected location""" - raise NotImplementedError - @pytest.fixture(scope="module") def app_args(self, download_test_data): """Returns additional application arguments from a test file. @@ -345,3 +335,16 @@ def test_folder_structure_same(self, dbcon, publish_finished, not_matched = expected.difference(published) assert not not_matched, "Missing {} files".format(not_matched) + + +class HostFixtures(PublishTest): + """Host specific fixtures. Should be implemented once per host.""" + @pytest.fixture(scope="module") + def last_workfile_path(self, download_test_data, output_folder_url): + """Returns url of workfile""" + raise NotImplementedError + + @pytest.fixture(scope="module") + def startup_scripts(self, monkeypatch_session, download_test_data): + """"Adds init scripts (like userSetup) to expected location""" + raise NotImplementedError \ No newline at end of file From c78d4d89a4a36b7987b0ba7d7fc175a282cf0593 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20LORRAIN?= Date: Mon, 13 Dec 2021 10:45:30 +0100 Subject: [PATCH 191/492] Handle message type attribute --- openpype/hosts/maya/plugins/publish/collect_look.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/hosts/maya/plugins/publish/collect_look.py b/openpype/hosts/maya/plugins/publish/collect_look.py index 20a9d4ca122..0ab278772ef 100644 --- a/openpype/hosts/maya/plugins/publish/collect_look.py +++ b/openpype/hosts/maya/plugins/publish/collect_look.py @@ -489,6 +489,8 @@ def collect_attributes_changed(self, instance): if not cmds.attributeQuery(attr, node=node, exists=True): continue attribute = "{}.{}".format(node, attr) + if cmds.getAttr(attribute, type=True) == "message": + continue node_attributes[attr] = cmds.getAttr(attribute) attributes.append({"name": node, From daa8eb532a2428d93376cf67a591c89319f9ac9d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 13 Dec 2021 11:49:19 +0100 Subject: [PATCH 192/492] OP-2042 - fix tested output path --- tests/lib/testing_classes.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/lib/testing_classes.py b/tests/lib/testing_classes.py index 06922abc013..fa467acf9c4 100644 --- a/tests/lib/testing_classes.py +++ b/tests/lib/testing_classes.py @@ -316,12 +316,14 @@ def test_folder_structure_same(self, dbcon, publish_finished, published_dir_base = download_test_data published_dir = os.path.join(output_folder_url, self.PROJECT, + self.ASSET, self.TASK, "**") expected_dir_base = os.path.join(published_dir_base, "expected") expected_dir = os.path.join(expected_dir_base, self.PROJECT, + self.ASSET, self.TASK, "**") print("Comparing published:'{}' : expected:'{}'".format(published_dir, From 165b5b0b0bb335511d70d68e8db4f2b6b8d4679b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20LORRAIN?= Date: Mon, 13 Dec 2021 11:59:43 +0100 Subject: [PATCH 193/492] Add zero padding to otio burnins --- openpype/scripts/otio_burnin.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/scripts/otio_burnin.py b/openpype/scripts/otio_burnin.py index 68f4728bc74..156550aab11 100644 --- a/openpype/scripts/otio_burnin.py +++ b/openpype/scripts/otio_burnin.py @@ -342,7 +342,8 @@ def _add_burnin(self, text, align, options, draw): if frame_start is None: replacement_final = replacement_size = str(MISSING_KEY_VALUE) else: - replacement_final = "%{eif:n+" + str(frame_start) + ":d}" + replacement_final = "%{eif:n+" + str(frame_start) + ":d:" + \ + str(len(str(frame_end))) + "}" replacement_size = str(frame_end) final_text = final_text.replace( From fed3013703b944142bc5f084b23b0705fbd0aaa0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 13 Dec 2021 12:37:33 +0100 Subject: [PATCH 194/492] fix changed state of wrapper label --- openpype/style/style.css | 57 +++++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/openpype/style/style.css b/openpype/style/style.css index 19245cdc402..81aa70ea62f 100644 --- a/openpype/style/style.css +++ b/openpype/style/style.css @@ -1044,16 +1044,45 @@ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { color: {color:settings:label-fg}; } #SettingsLabel:hover {color: {color:settings:label-fg-hover};} -#SettingsLabel[state="studio"] {color: {color:settings:studio-light};} -#SettingsLabel[state="studio"]:hover {color: {color:settings:studio-label-hover};} -#SettingsLabel[state="modified"] {color: {color:settings:modified-mid};} -#SettingsLabel[state="modified"]:hover {color: {color:settings:modified-light};} -#SettingsLabel[state="overriden-modified"] {color: {color:settings:modified-mid};} -#SettingsLabel[state="overriden-modified"]:hover {color: {color:settings:modified-light};} -#SettingsLabel[state="overriden"] {color: {color:settings:project-mid};} -#SettingsLabel[state="overriden"]:hover {color: {color:settings:project-light};} -#SettingsLabel[state="invalid"] {color:{color:settings:invalid-dark};} -#SettingsLabel[state="invalid"]:hover {color: {color:settings:invalid-dark};} + +#ExpandLabel { + font-weight: bold; + color: {color:settings:label-fg}; +} +#ExpandLabel:hover { + color: {color:settings:label-fg-hover}; +} + +#ExpandLabel[state="studio"], #SettingsLabel[state="studio"] { + color: {color:settings:studio-light}; +} +#ExpandLabel[state="studio"]:hover, #SettingsLabel[state="studio"]:hover { + color: {color:settings:studio-label-hover}; +} +#ExpandLabel[state="modified"], #SettingsLabel[state="modified"] { + color: {color:settings:modified-mid}; +} +#ExpandLabel[state="modified"]:hover, #SettingsLabel[state="modified"]:hover { + color: {color:settings:modified-light}; +} +#ExpandLabel[state="overriden-modified"], #SettingsLabel[state="overriden-modified"] { + color: {color:settings:modified-mid}; +} +#ExpandLabel[state="overriden-modified"]:hover, #SettingsLabel[state="overriden-modified"]:hover { + color: {color:settings:modified-light}; +} +#ExpandLabel[state="overriden"], #SettingsLabel[state="overriden"] {color: { + color:settings:project-mid}; +} +#ExpandLabel[state="overriden"]:hover, #SettingsLabel[state="overriden"]:hover { + color: {color:settings:project-light}; +} +#ExpandLabel[state="invalid"], #SettingsLabel[state="invalid"] { + color:{color:settings:invalid-dark}; +} +#ExpandLabel[state="invalid"]:hover, #SettingsLabel[state="invalid"]:hover { + color: {color:settings:invalid-dark}; +} /* TODO Replace these with explicit widget types if possible */ #SettingsMainWidget QWidget[input-state="modified"] { @@ -1085,14 +1114,6 @@ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { #DictKey[state="modified"] {border-color: {color:settings:modified-mid};} #DictKey[state="invalid"] {border-color: {color:settings:invalid-dark};} -#ExpandLabel { - font-weight: bold; - color: {color:settings:label-fg}; -} -#ExpandLabel:hover { - color: {color:settings:label-fg-hover}; -} - #ContentWidget { background-color: transparent; } From 590f19e96f0b6a903cb760157a50ed55f0a782c3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 13 Dec 2021 12:44:37 +0100 Subject: [PATCH 195/492] fixed typo --- openpype/style/style.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/style/style.css b/openpype/style/style.css index 81aa70ea62f..4159fe1676f 100644 --- a/openpype/style/style.css +++ b/openpype/style/style.css @@ -1071,8 +1071,8 @@ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { #ExpandLabel[state="overriden-modified"]:hover, #SettingsLabel[state="overriden-modified"]:hover { color: {color:settings:modified-light}; } -#ExpandLabel[state="overriden"], #SettingsLabel[state="overriden"] {color: { - color:settings:project-mid}; +#ExpandLabel[state="overriden"], #SettingsLabel[state="overriden"] { + color: {color:settings:project-mid}; } #ExpandLabel[state="overriden"]:hover, #SettingsLabel[state="overriden"]:hover { color: {color:settings:project-light}; From 4013412c638f7bafb050e941b757ca69ddf7a2c2 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 13 Dec 2021 13:42:40 +0100 Subject: [PATCH 196/492] send_selection returns True --- openpype/hosts/flame/api/menu.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/hosts/flame/api/menu.py b/openpype/hosts/flame/api/menu.py index 85fcb86026f..f216428e18b 100644 --- a/openpype/hosts/flame/api/menu.py +++ b/openpype/hosts/flame/api/menu.py @@ -28,6 +28,7 @@ def send_selection(selection): import openpype.hosts.flame as opflame opflame.selection = selection print(opflame.selection) + return True class _FlameMenuApp(object): def __init__(self, framework): From c4106bf95a9e604c760c1c449ef456c884b27f40 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 13 Dec 2021 14:01:28 +0100 Subject: [PATCH 197/492] set placeholder font color on initialization instead of after show --- openpype/tools/utils/widgets.py | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/openpype/tools/utils/widgets.py b/openpype/tools/utils/widgets.py index 009c1dc5064..3bfa092a210 100644 --- a/openpype/tools/utils/widgets.py +++ b/openpype/tools/utils/widgets.py @@ -12,22 +12,17 @@ class PlaceholderLineEdit(QtWidgets.QLineEdit): """Set placeholder color of QLineEdit in Qt 5.12 and higher.""" def __init__(self, *args, **kwargs): super(PlaceholderLineEdit, self).__init__(*args, **kwargs) - self._first_show = True - - def showEvent(self, event): - super(PlaceholderLineEdit, self).showEvent(event) - if self._first_show: - self._first_show = False + # Change placeholder palette color + if hasattr(QtGui.QPalette, "PlaceholderText"): filter_palette = self.palette() - if hasattr(filter_palette, "PlaceholderText"): - color_obj = get_objected_colors()["font"] - color = color_obj.get_qcolor() - color.setAlpha(67) - filter_palette.setColor( - filter_palette.PlaceholderText, - color - ) - self.setPalette(filter_palette) + color_obj = get_objected_colors()["font"] + color = color_obj.get_qcolor() + color.setAlpha(67) + filter_palette.setColor( + QtGui.QPalette.PlaceholderText, + color + ) + self.setPalette(filter_palette) class ImageButton(QtWidgets.QPushButton): From 0b6e2f337aa94e0aa35beb17937a2d7a49fd889b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 6 Dec 2021 12:24:02 +0100 Subject: [PATCH 198/492] modified version regex for installation of PySide2 into blender --- openpype/hosts/blender/hooks/pre_pyside_install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/blender/hooks/pre_pyside_install.py b/openpype/hosts/blender/hooks/pre_pyside_install.py index 6d253300d9f..e2a419c8ef5 100644 --- a/openpype/hosts/blender/hooks/pre_pyside_install.py +++ b/openpype/hosts/blender/hooks/pre_pyside_install.py @@ -32,7 +32,7 @@ def execute(self): def inner_execute(self): # Get blender's python directory - version_regex = re.compile(r"^2\.[0-9]{2}$") + version_regex = re.compile(r"^[2-3]\.[0-9]+$") executable = self.launch_context.executable.executable_path if os.path.basename(executable).lower() != "blender.exe": From 818740c359b336925fe4b8e662ca610c75fbfe75 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 13 Dec 2021 19:23:59 +0100 Subject: [PATCH 199/492] use default components in log mongo --- openpype/lib/log.py | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/openpype/lib/log.py b/openpype/lib/log.py index 85cbc733ba5..a34cb898e32 100644 --- a/openpype/lib/log.py +++ b/openpype/lib/log.py @@ -27,7 +27,7 @@ from . import Terminal from .mongo import ( MongoEnvNotSet, - decompose_url, + get_default_components, OpenPypeMongoConnection ) try: @@ -202,10 +202,6 @@ class PypeLogger: use_mongo_logging = None mongo_process_id = None - # Information about mongo url - log_mongo_url = None - log_mongo_url_components = None - # Database name in Mongo log_database_name = os.environ["OPENPYPE_DATABASE_NAME"] # Collection name under database in Mongo @@ -282,9 +278,9 @@ def _get_mongo_handler(cls): if not cls.use_mongo_logging: return - components = cls.log_mongo_url_components + components = get_default_components() kwargs = { - "host": cls.log_mongo_url, + "host": components["host"], "database_name": cls.log_database_name, "collection": cls.log_collection_name, "username": components["username"], @@ -354,14 +350,8 @@ def _initialize(cls): # Define if is in OPENPYPE_DEBUG mode cls.pype_debug = int(os.getenv("OPENPYPE_DEBUG") or "0") - # Mongo URL where logs will be stored - cls.log_mongo_url = os.environ.get("OPENPYPE_MONGO") - - if not cls.log_mongo_url: + if not os.environ.get("OPENPYPE_MONGO"): cls.use_mongo_logging = False - else: - # Decompose url - cls.log_mongo_url_components = decompose_url(cls.log_mongo_url) # Mark as initialized cls.initialized = True @@ -474,7 +464,7 @@ def get_log_mongo_connection(cls): if not cls.initialized: cls.initialize() - return OpenPypeMongoConnection.get_mongo_client(cls.log_mongo_url) + return OpenPypeMongoConnection.get_mongo_client() def timeit(method): From f712b207f20456621511052da6c6a2d7bd706102 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 13 Dec 2021 19:25:08 +0100 Subject: [PATCH 200/492] removed compose_url and decompose_url from api and lib functions --- openpype/api.py | 4 ---- openpype/lib/__init__.py | 4 ---- 2 files changed, 8 deletions(-) diff --git a/openpype/api.py b/openpype/api.py index a6529202ff2..51854492abb 100644 --- a/openpype/api.py +++ b/openpype/api.py @@ -31,8 +31,6 @@ ) from .lib.mongo import ( - decompose_url, - compose_url, get_default_components ) @@ -84,8 +82,6 @@ "Anatomy", "config", "execute", - "decompose_url", - "compose_url", "get_default_components", "ApplicationManager", "BuildWorkfile", diff --git a/openpype/lib/__init__.py b/openpype/lib/__init__.py index efd2cddf7e1..c99e3bc28d7 100644 --- a/openpype/lib/__init__.py +++ b/openpype/lib/__init__.py @@ -32,8 +32,6 @@ ) from .log import PypeLogger, timeit from .mongo import ( - decompose_url, - compose_url, get_default_components, validate_mongo_connection, OpenPypeMongoConnection @@ -276,8 +274,6 @@ "get_datetime_data", "PypeLogger", - "decompose_url", - "compose_url", "get_default_components", "validate_mongo_connection", "OpenPypeMongoConnection", From 975f0a1c6857fb950e176fc70560bd7929feb8cb Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 13 Dec 2021 19:25:17 +0100 Subject: [PATCH 201/492] removed unused compose_url --- openpype/lib/mongo.py | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/openpype/lib/mongo.py b/openpype/lib/mongo.py index 0fd4517b5b0..9f2463eb893 100644 --- a/openpype/lib/mongo.py +++ b/openpype/lib/mongo.py @@ -48,35 +48,6 @@ def decompose_url(url): return components -def compose_url(scheme=None, - host=None, - username=None, - password=None, - port=None, - auth_db=None): - - url = "{scheme}://" - - if username and password: - url += "{username}:{password}@" - - url += "{host}" - if port: - url += ":{port}" - - if auth_db: - url += "?authSource={auth_db}" - - return url.format(**{ - "scheme": scheme, - "host": host, - "username": username, - "password": password, - "port": port, - "auth_db": auth_db - }) - - def get_default_components(): mongo_url = os.environ.get("OPENPYPE_MONGO") if mongo_url is None: From 432c9f8f15377f506b5895d7f4433c2946c6ca9f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 13 Dec 2021 19:27:31 +0100 Subject: [PATCH 202/492] decompose_url is now private function _decompose_url --- openpype/lib/mongo.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/openpype/lib/mongo.py b/openpype/lib/mongo.py index 9f2463eb893..fb4eb76f28a 100644 --- a/openpype/lib/mongo.py +++ b/openpype/lib/mongo.py @@ -15,7 +15,14 @@ class MongoEnvNotSet(Exception): pass -def decompose_url(url): +def _decompose_url(url): + """Decompose mongo url to basic components. + + Used for creation of MongoHandler which expect mongo url components as + separated kwargs. Components are at the end not used as we're setting + connection directly this is just a dumb components for MongoHandler + validation pass. + """ components = { "scheme": None, "host": None, @@ -54,7 +61,7 @@ def get_default_components(): raise MongoEnvNotSet( "URL for Mongo logging connection is not set." ) - return decompose_url(mongo_url) + return _decompose_url(mongo_url) def should_add_certificate_path_to_mongo_url(mongo_url): From d83989c9b31be293d52de4e8a210d6e5f09a6c6d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 13 Dec 2021 19:28:14 +0100 Subject: [PATCH 203/492] use first part of url for replica set urls --- openpype/lib/mongo.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openpype/lib/mongo.py b/openpype/lib/mongo.py index fb4eb76f28a..7e0bd4f7969 100644 --- a/openpype/lib/mongo.py +++ b/openpype/lib/mongo.py @@ -23,6 +23,11 @@ def _decompose_url(url): connection directly this is just a dumb components for MongoHandler validation pass. """ + # Use first url from passed url + # - this is beacuse it is possible to pass multiple urls for multiple + # replica sets which would crash on urlparse otherwise + # - please don't use comma in username of password + url = url.split(",")[0] components = { "scheme": None, "host": None, From f52d4700ae75b26ded49adbcadc1ad5818555b39 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 13 Dec 2021 20:10:36 +0100 Subject: [PATCH 204/492] added new plugin that will cleanup paths defined in context --- openpype/plugins/publish/cleanup_explicit.py | 152 +++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 openpype/plugins/publish/cleanup_explicit.py diff --git a/openpype/plugins/publish/cleanup_explicit.py b/openpype/plugins/publish/cleanup_explicit.py new file mode 100644 index 00000000000..88bba345323 --- /dev/null +++ b/openpype/plugins/publish/cleanup_explicit.py @@ -0,0 +1,152 @@ +# -*- coding: utf-8 -*- +"""Cleanup files when publishing is done.""" +import os +import shutil +import pyblish.api + + +class ExplicitCleanUp(pyblish.api.ContextPlugin): + """Cleans up the files and folder defined to be deleted. + + plugin is looking for 2 keys into context data: + - `cleanupFullPaths` - full paths that should be removed not matter if + is path to file or to directory + - `cleanupEmptyDirs` - full paths to directories that should be removed + only if do not contain any file in it but will be removed if contain + sub-folders + """ + + order = pyblish.api.IntegratorOrder + 10 + label = "Explicit Clean Up" + optional = True + active = True + + def process(self, context): + cleanup_full_paths = context.data.get("cleanupFullPaths") + cleanup_empty_dirs = context.data.get("cleanupEmptyDirs") + + self._remove_full_paths(cleanup_full_paths) + self._remove_empty_dirs(cleanup_empty_dirs) + + def _remove_full_paths(self, full_paths): + """Remove files and folders from disc. + + Folders are removed with whole content. + """ + if not full_paths: + self.log.debug("No full paths to cleanup were collected.") + return + + # Separate paths into files and directories + filepaths = set() + dirpaths = set() + for path in full_paths: + # Skip empty items + if not path: + continue + # Normalize path + normalized = os.path.normpath(path) + # Check if path exists + if not os.path.exists(normalized): + continue + + if os.path.isfile(normalized): + filepaths.add(normalized) + else: + dirpaths.add(normalized) + + # Store failed paths with exception + failed = [] + # Store removed filepaths for logging + succeded_files = set() + # Remove file by file + for filepath in filepaths: + try: + os.remove(filepath) + succeded_files.add(filepath) + except Exception as exc: + failed.append((filepath, exc)) + + if succeded_files: + self.log.info( + "Removed files:\n{}".format("\n".join(succeded_files)) + ) + + # Delete folders with it's content + succeded_dirs = set() + for dirpath in dirpaths: + # Check if directory still exists + # - it is possible that directory was already deleted with + # different dirpath to delete + if os.path.exists(dirpath): + try: + shutil.rmtree(dirpath) + succeded_dirs.add(dirpath) + except Exception: + failed.append(dirpath) + + if succeded_dirs: + self.log.info( + "Removed direcoties:\n{}".format("\n".join(succeded_dirs)) + ) + + # Prepare lines for report of failed removements + lines = [] + for filepath, exc in failed: + lines.append("{}: {}".format(filepath, str(exc))) + + if lines: + self.log.warning( + "Failed to remove filepaths:\n{}".format("\n".join(lines)) + ) + + def _remove_empty_dirs(self, empty_dirpaths): + """Remove directories if do not contain any files.""" + if not empty_dirpaths: + self.log.debug("No empty dirs to cleanup were collected.") + return + + # First filtering of directories and making sure those are + # existing directories + filtered_dirpaths = set() + for path in empty_dirpaths: + if ( + path + and os.path.exists(path) + and os.path.isdir(path) + ): + filtered_dirpaths.add(os.path.normpath(path)) + + to_delete_dirpaths = set() + to_skip_dirpaths = set() + # Check if contain any files (or it's subfolders contain files) + for dirpath in filtered_dirpaths: + valid = True + for _, _, filenames in os.walk(dirpath): + if filenames: + valid = False + break + + if valid: + to_delete_dirpaths.add(dirpath) + else: + to_skip_dirpaths.add(dirpath) + + if to_skip_dirpaths: + self.log.debug( + "Skipped directories because contain files:\n{}".format( + "\n".join(to_skip_dirpaths) + ) + ) + + # Remove empty directies + for dirpath in to_delete_dirpaths: + if os.path.exists(dirpath): + shutil.rmtree(dirpath) + + if to_delete_dirpaths: + self.log.debug( + "Deleted empty directories:\n{}".format( + "\n".join(to_delete_dirpaths) + ) + ) From 6e64e385544bb439e81eb9a8712275f094620d7b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 13 Dec 2021 20:16:48 +0100 Subject: [PATCH 205/492] added hosts filter for old cleanup plugin --- openpype/plugins/publish/cleanup.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/openpype/plugins/publish/cleanup.py b/openpype/plugins/publish/cleanup.py index b8104078d9f..f29e6ccd4e8 100644 --- a/openpype/plugins/publish/cleanup.py +++ b/openpype/plugins/publish/cleanup.py @@ -15,6 +15,25 @@ class CleanUp(pyblish.api.InstancePlugin): order = pyblish.api.IntegratorOrder + 10 label = "Clean Up" + hosts = [ + "aftereffects", + "blender", + "celaction", + "flame", + "fusion", + "harmony", + "hiero", + "houdini", + "maya", + "nuke", + "photoshop", + "resolve", + "tvpaint", + "unreal", + "standalonepublisher", + "webpublisher", + "shell" + ] exclude_families = ["clip"] optional = True active = True From 148bb47b7d2c2ad366b2271a1c6916016651fdb2 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 14 Dec 2021 10:54:10 +0100 Subject: [PATCH 206/492] OP-2053 - allow injection of AVALON_DB env var as a db --- start.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/start.py b/start.py index 0f7e82071d7..10b81bc7256 100644 --- a/start.py +++ b/start.py @@ -339,13 +339,14 @@ def set_avalon_environments(): os.environ.get("AVALON_MONGO") or os.environ["OPENPYPE_MONGO"] ) + avalon_db = os.environ.get("AVALON_DB") or "avalon" # for tests os.environ.update({ # Mongo url (use same as OpenPype has) "AVALON_MONGO": avalon_mongo_url, "AVALON_SCHEMA": schema_path, # Mongo DB name where avalon docs are stored - "AVALON_DB": "avalon", + "AVALON_DB": avalon_db, # Name of config "AVALON_CONFIG": "openpype", "AVALON_LABEL": "OpenPype" From a6a7348a73ac5249cbbcd143b8e0f9678c4b0b6e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 14 Dec 2021 11:12:06 +0100 Subject: [PATCH 207/492] fix access to default settings in get_general_environments --- openpype/settings/lib.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/settings/lib.py b/openpype/settings/lib.py index ff755624132..43489aecfd1 100644 --- a/openpype/settings/lib.py +++ b/openpype/settings/lib.py @@ -933,8 +933,10 @@ def get_general_environments(): # - prevent to use `get_system_settings` where `get_default_settings` # is used default_values = load_openpype_default_settings() + system_settings = default_values["system_settings"] studio_overrides = get_studio_system_settings_overrides() - result = apply_overrides(default_values, studio_overrides) + + result = apply_overrides(system_settings, studio_overrides) environments = result["general"]["environment"] clear_metadata_from_settings(environments) From 8739dd9f05e7f70dddd388da2de0b7efd4456832 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 14 Dec 2021 11:13:02 +0100 Subject: [PATCH 208/492] OP-2053 - fix counts in db_asserts --- .../hosts/aftereffects/test_publish_in_aftereffects.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/hosts/aftereffects/test_publish_in_aftereffects.py b/tests/integration/hosts/aftereffects/test_publish_in_aftereffects.py index e0f6b3e48e0..ec0280e7c6a 100644 --- a/tests/integration/hosts/aftereffects/test_publish_in_aftereffects.py +++ b/tests/integration/hosts/aftereffects/test_publish_in_aftereffects.py @@ -68,7 +68,7 @@ def startup_scripts(self, monkeypatch_session, download_test_data): def test_db_asserts(self, dbcon, publish_finished): """Host and input data dependent expected results in DB.""" print("test_db_asserts") - assert 3 == dbcon.count_documents({"type": "version"}), \ + assert 2 == dbcon.count_documents({"type": "version"}), \ "Not expected no of versions" assert 0 == dbcon.count_documents({"type": "version", @@ -88,7 +88,7 @@ def test_db_asserts(self, dbcon, publish_finished): "name": "reviewTesttask"}), \ "reviewTesttask subset must be present" - assert 6 == dbcon.count_documents({"type": "representation"}), \ + assert 4 == dbcon.count_documents({"type": "representation"}), \ "Not expected no of representations" assert 1 == dbcon.count_documents({"type": "representation", From 02717fac4eaaf5dbd17ddcf86e59bc58b136a8ff Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 14 Dec 2021 11:15:51 +0100 Subject: [PATCH 209/492] Update error msg format Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- .../plugins/publish/collect_extension_version.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/aftereffects/plugins/publish/collect_extension_version.py b/openpype/hosts/aftereffects/plugins/publish/collect_extension_version.py index 3352fd21f0e..4e74252043a 100644 --- a/openpype/hosts/aftereffects/plugins/publish/collect_extension_version.py +++ b/openpype/hosts/aftereffects/plugins/publish/collect_extension_version.py @@ -48,9 +48,9 @@ def process(self, context): expected_version = found[0][1] if expected_version != installed_version: - msg = "Expected version '{}' found '{}'\n".format( - expected_version, installed_version) - msg += "Please update your installed extension, it might not work " - msg += "properly." + msg = ( + "Expected version '{}' found '{}'\n Please update" + " your installed extension, it might not work properly." + ).format(expected_version, installed_version) raise ValueError(msg) From aa232b43268cd38a0162eb6af6d50a8d4b1d998c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 14 Dec 2021 11:13:02 +0100 Subject: [PATCH 210/492] OP-2053 - fix counts in db_asserts --- .../hosts/aftereffects/test_publish_in_aftereffects.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/integration/hosts/aftereffects/test_publish_in_aftereffects.py b/tests/integration/hosts/aftereffects/test_publish_in_aftereffects.py index e0f6b3e48e0..c3ca9aa9d23 100644 --- a/tests/integration/hosts/aftereffects/test_publish_in_aftereffects.py +++ b/tests/integration/hosts/aftereffects/test_publish_in_aftereffects.py @@ -68,7 +68,7 @@ def startup_scripts(self, monkeypatch_session, download_test_data): def test_db_asserts(self, dbcon, publish_finished): """Host and input data dependent expected results in DB.""" print("test_db_asserts") - assert 3 == dbcon.count_documents({"type": "version"}), \ + assert 2 == dbcon.count_documents({"type": "version"}), \ "Not expected no of versions" assert 0 == dbcon.count_documents({"type": "version", @@ -81,18 +81,18 @@ def test_db_asserts(self, dbcon, publish_finished): "modelMain subset must be present" assert 1 == dbcon.count_documents({"type": "subset", - "name": "workfileTesttask"}), \ + "name": "workfileTest_task"}), \ "workfileTesttask subset must be present" assert 1 == dbcon.count_documents({"type": "subset", "name": "reviewTesttask"}), \ "reviewTesttask subset must be present" - assert 6 == dbcon.count_documents({"type": "representation"}), \ + assert 4 == dbcon.count_documents({"type": "representation"}), \ "Not expected no of representations" assert 1 == dbcon.count_documents({"type": "representation", - "context.subset": "imageMainBackgroundcopy", # noqa E501 + "context.subset": "renderTestTaskDefault", # noqa E501 "context.ext": "png"}), \ "Not expected no of representations with ext 'png'" From 0ff12fdaeeb6629e74a26cd56c06fa4d459ddd8d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 14 Dec 2021 11:48:23 +0100 Subject: [PATCH 211/492] cache object types --- .../default_modules/ftrack/lib/avalon_sync.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/openpype/modules/default_modules/ftrack/lib/avalon_sync.py b/openpype/modules/default_modules/ftrack/lib/avalon_sync.py index 3ba874281ac..392b64eb574 100644 --- a/openpype/modules/default_modules/ftrack/lib/avalon_sync.py +++ b/openpype/modules/default_modules/ftrack/lib/avalon_sync.py @@ -360,6 +360,8 @@ def launch_setup(self, project_full_name): self._subsets_by_parent_id = None self._changeability_by_mongo_id = None + self._object_types_by_name = None + self.all_filtered_entities = {} self.filtered_ids = [] self.not_selected_ids = [] @@ -651,6 +653,18 @@ def changeability_by_mongo_id(self): self._bubble_changeability(list(self.subsets_by_parent_id.keys())) return self._changeability_by_mongo_id + @property + def object_types_by_name(self): + if self._object_types_by_name is None: + object_types_by_name = self.session.query( + "select id, name from ObjectType" + ).all() + self._object_types_by_name = { + object_type["name"]: object_type + for object_type in object_types_by_name + } + return self._object_types_by_name + @property def all_ftrack_names(self): """ @@ -880,10 +894,7 @@ def set_cutom_attributes(self): custom_attrs, hier_attrs = get_openpype_attr( self.session, query_keys=self.cust_attr_query_keys ) - ent_types = self.session.query("select id, name from ObjectType").all() - ent_types_by_name = { - ent_type["name"]: ent_type["id"] for ent_type in ent_types - } + ent_types_by_name = self.object_types_by_name # Custom attribute types cust_attr_types = self.session.query( "select id, name from CustomAttributeType" From 00b143066ea99ffabd32ea7bf32b711d4f07a849 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 14 Dec 2021 11:48:55 +0100 Subject: [PATCH 212/492] check existence of entity type on recreation and use Folder if not found --- .../modules/default_modules/ftrack/lib/avalon_sync.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/openpype/modules/default_modules/ftrack/lib/avalon_sync.py b/openpype/modules/default_modules/ftrack/lib/avalon_sync.py index 392b64eb574..f58eb914853 100644 --- a/openpype/modules/default_modules/ftrack/lib/avalon_sync.py +++ b/openpype/modules/default_modules/ftrack/lib/avalon_sync.py @@ -2502,7 +2502,13 @@ def create_ftrack_ent_from_avalon_ent(self, av_entity, parent_id): parent_entity = self.entities_dict[parent_id]["entity"] _name = av_entity["name"] - _type = av_entity["data"].get("entityType", "folder") + _type = av_entity["data"].get("entityType") + # Check existence of object type + if _type and _type not in self.object_types_by_name: + _type = None + + if not _type: + _type = "Folder" self.log.debug(( "Re-ceating deleted entity {} <{}>" From f1ab759a6179efbd96f4db17957351f60984fb04 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 14 Dec 2021 13:28:00 +0100 Subject: [PATCH 213/492] OP-2151 - replaced PATH usage with oiio path for maketx utility --- openpype/hosts/maya/plugins/publish/extract_look.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_look.py b/openpype/hosts/maya/plugins/publish/extract_look.py index 2407617b6f6..92587c29101 100644 --- a/openpype/hosts/maya/plugins/publish/extract_look.py +++ b/openpype/hosts/maya/plugins/publish/extract_look.py @@ -55,8 +55,11 @@ def maketx(source, destination, *args): str: Output of `maketx` command. """ + from openpype.lib import get_oiio_tools_path + + maketx_path = get_oiio_tools_path("maketx") cmd = [ - "maketx", + maketx_path, "-v", # verbose "-u", # update mode # unpremultiply before conversion (recommended when alpha present) From 209dbea1a5b83aa61047b072923565fe238c56ff Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 14 Dec 2021 13:44:21 +0100 Subject: [PATCH 214/492] OP-2151 - added logging and exception if oiio utilities not found --- openpype/hosts/maya/plugins/publish/extract_look.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openpype/hosts/maya/plugins/publish/extract_look.py b/openpype/hosts/maya/plugins/publish/extract_look.py index 92587c29101..953539f65c9 100644 --- a/openpype/hosts/maya/plugins/publish/extract_look.py +++ b/openpype/hosts/maya/plugins/publish/extract_look.py @@ -58,6 +58,11 @@ def maketx(source, destination, *args): from openpype.lib import get_oiio_tools_path maketx_path = get_oiio_tools_path("maketx") + if not os.path.exists(maketx_path): + print( + "OIIO tool not found in {}".format(maketx_path)) + raise AssertionError("OIIO tool not found") + cmd = [ maketx_path, "-v", # verbose From ebbd43bd5fd6d587f6e88558df5512d8c9e971d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= <33513211+antirotor@users.noreply.github.com> Date: Tue, 14 Dec 2021 13:46:34 +0100 Subject: [PATCH 215/492] Update Dockerfile.centos7 Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- Dockerfile.centos7 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.centos7 b/Dockerfile.centos7 index ce60ea7fb19..736a42663c4 100644 --- a/Dockerfile.centos7 +++ b/Dockerfile.centos7 @@ -42,7 +42,7 @@ RUN yum -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.n ncurses-devel \ qt5-qtbase-devel \ xcb-util-wm \ - xcb-util-renderutil + xcb-util-renderutil \ && yum clean all # we need to build our own patchelf From 12282ca19cb931ea5499b0665a74e6e830df6eb4 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 14 Dec 2021 14:54:23 +0100 Subject: [PATCH 216/492] nuke: baking representations was not additive --- .../nuke/plugins/publish/extract_review_data_mov.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py b/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py index 261fca65831..32962b57a66 100644 --- a/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py +++ b/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py @@ -42,6 +42,7 @@ def process(self, instance): # generate data with anlib.maintained_selection(): + generated_repres = [] for o_name, o_data in self.outputs.items(): f_families = o_data["filter"]["families"] f_task_types = o_data["filter"]["task_types"] @@ -112,11 +113,13 @@ def process(self, instance): }) else: data = exporter.generate_mov(**o_data) + generated_repres.extend(data["representations"]) - self.log.info(data["representations"]) + self.log.info(generated_repres) - # assign to representations - instance.data["representations"] += data["representations"] + if generated_repres: + # assign to representations + instance.data["representations"] += generated_repres self.log.debug( "_ representations: {}".format( From b57a09b430a8dd1e09db2fa97cf37685b0ca9aa8 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 14 Dec 2021 15:16:48 +0100 Subject: [PATCH 217/492] OP-2019 - revert unwanted commit --- openpype/hooks/pre_foundry_apps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hooks/pre_foundry_apps.py b/openpype/hooks/pre_foundry_apps.py index 70554cbedbf..85f68c6b604 100644 --- a/openpype/hooks/pre_foundry_apps.py +++ b/openpype/hooks/pre_foundry_apps.py @@ -13,7 +13,7 @@ class LaunchFoundryAppsWindows(PreLaunchHook): # Should be as last hook because must change launch arguments to string order = 1000 - app_groups = ["nuke", "nukex", "hiero", "nukestudio", "aftereffects"] + app_groups = ["nuke", "nukex", "hiero", "nukestudio"] platforms = ["windows"] def execute(self): From 450dbf3fd25225b4d94a6f92f6404ccb5a0c80c3 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 14 Dec 2021 15:29:42 +0100 Subject: [PATCH 218/492] OP-2053 - fix PS after merge --- .../photoshop/test_publish_in_photoshop.py | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/tests/integration/hosts/photoshop/test_publish_in_photoshop.py b/tests/integration/hosts/photoshop/test_publish_in_photoshop.py index b52af480091..f7bedf60699 100644 --- a/tests/integration/hosts/photoshop/test_publish_in_photoshop.py +++ b/tests/integration/hosts/photoshop/test_publish_in_photoshop.py @@ -44,32 +44,6 @@ class TestPublishInPhotoshop(PhotoshopTestClass): TIMEOUT = 120 # publish timeout - @pytest.fixture(scope="module") - def last_workfile_path(self, download_test_data): - """Get last_workfile_path from source data. - - Maya expects workfile in proper folder, so copy is done first. - """ - src_path = os.path.join(download_test_data, - "input", - "workfile", - "test_project_test_asset_TestTask_v001.psd") - dest_folder = os.path.join(download_test_data, - self.PROJECT, - self.ASSET, - "work", - self.TASK) - os.makedirs(dest_folder) - dest_path = os.path.join(dest_folder, - "test_project_test_asset_TestTask_v001.psd") - shutil.copy(src_path, dest_path) - - yield dest_path - - @pytest.fixture(scope="module") - def startup_scripts(self, monkeypatch_session, download_test_data): - """Points Maya to userSetup file from input data""" - pass def test_db_asserts(self, dbcon, publish_finished): """Host and input data dependent expected results in DB.""" From 2c66a1eab2527a34029fc639fc475efa2dc77896 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 14 Dec 2021 15:31:03 +0100 Subject: [PATCH 219/492] OP-2053 - fix PS after merge --- tests/integration/hosts/photoshop/test_publish_in_photoshop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/hosts/photoshop/test_publish_in_photoshop.py b/tests/integration/hosts/photoshop/test_publish_in_photoshop.py index f7bedf60699..32053cd9d4f 100644 --- a/tests/integration/hosts/photoshop/test_publish_in_photoshop.py +++ b/tests/integration/hosts/photoshop/test_publish_in_photoshop.py @@ -37,7 +37,7 @@ class TestPublishInPhotoshop(PhotoshopTestClass): ] APP = "photoshop" - # keep empty to locate latest installed variant or explicit + # keep empty to locate latest installed variant or explicit APP_VARIANT = "" APP_NAME = "{}/{}".format(APP, APP_VARIANT) From 68b99391acecec1a208468df98026506db7ac95a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 14 Dec 2021 15:35:39 +0100 Subject: [PATCH 220/492] modified message --- openpype/tools/settings/settings/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/settings/settings/base.py b/openpype/tools/settings/settings/base.py index 9016e639706..b10c9588809 100644 --- a/openpype/tools/settings/settings/base.py +++ b/openpype/tools/settings/settings/base.py @@ -340,7 +340,7 @@ def _apply_values_from_project(self, project_name): # TODO better message title = "Applying values failed" - msg = "Using values from project \"{}\" failed.".format( + msg = "Applying values from project \"{}\" failed.".format( project_name ) detail_msg = "".join( From 7dc4264d834dd4262a5ebeeca5552a0d9c1c5062 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 14 Dec 2021 16:46:25 +0100 Subject: [PATCH 221/492] flame otio export wip --- openpype/hosts/flame/otio/flame_export.py | 214 +++++++++++++--------- 1 file changed, 132 insertions(+), 82 deletions(-) diff --git a/openpype/hosts/flame/otio/flame_export.py b/openpype/hosts/flame/otio/flame_export.py index af4322e3d92..13199fe6c72 100644 --- a/openpype/hosts/flame/otio/flame_export.py +++ b/openpype/hosts/flame/otio/flame_export.py @@ -5,17 +5,16 @@ import re import sys import ast -from compiler.ast import flatten + import opentimelineio as otio from . import utils -import hiero.core -import hiero.ui +import flame self = sys.modules[__name__] -self.track_types = { - hiero.core.VideoTrack: otio.schema.TrackKind.Video, - hiero.core.AudioTrack: otio.schema.TrackKind.Audio -} +# self.track_types = { +# hiero.core.VideoTrack: otio.schema.TrackKind.Video, +# hiero.core.AudioTrack: otio.schema.TrackKind.Audio +# } self.project_fps = None self.marker_color_map = { "magenta": otio.schema.MarkerColor.MAGENTA, @@ -29,17 +28,17 @@ self.include_tags = True -def get_current_hiero_project(remove_untitled=False): - projects = flatten(hiero.core.projects()) - if not remove_untitled: - return next(iter(projects)) - - # if remove_untitled - for proj in projects: - if "Untitled" in proj.name(): - proj.close() +def flatten(_list): + for item in _list: + if isinstance(item, (list, tuple)): + for sub_item in flatten(item): + yield sub_item else: - return proj + yield item + + +def get_current_flame_project(): + return flame.project.current_project def create_otio_rational_time(frame, fps): @@ -313,7 +312,7 @@ def create_otio_gap(gap_start, clip_start, tl_start_frame, fps): def _create_otio_timeline(): - project = get_current_hiero_project(remove_untitled=False) + project = get_current_flame_project() metadata = _get_metadata(self.timeline) metadata.update({ @@ -375,73 +374,124 @@ def add_otio_metadata(otio_item, media_source, **kwargs): for key, value in metadata.items(): otio_item.metadata.update({key: value}) - -def create_otio_timeline(): - - def set_prev_item(itemindex, track_item): - # Add Gap if needed - if itemindex == 0: - # if it is first track item at track then add - # it to previouse item - return track_item - - else: - # get previouse item - return track_item.parent().items()[itemindex - 1] - - # get current timeline - self.timeline = hiero.ui.activeSequence() - self.project_fps = self.timeline.framerate().toFloat() - - # convert timeline to otio - otio_timeline = _create_otio_timeline() - - # loop all defined track types - for track in self.timeline.items(): - # skip if track is disabled - if not track.isEnabled(): +def get_segment_attributes(segment, frame_rate): + print(segment.attributes) + if str(segment.name)[1:-1] == "": + return None + + # get clip frame duration + record_duration = str(segment.record_duration)[1:-1] + clip_duration = utils.timecode_to_frames( + record_duration, frame_rate) + + # populate shot source metadata + shot_description = "" + for attr in ["tape_name", "source_name", "head", + "tail", "file_path"]: + if not hasattr(segment, attr): continue + _value = getattr(segment, attr) + _label = attr.replace("_", " ").capitalize() + row = "{}: {}\n".format(_label, _value) + shot_description += row + + # Add timeline segment to tree + clip_data = { + "clip_name": str(segment.name)[1:-1], + "clip_duration": str(clip_duration), + "clip_comment": str(segment.comment)[1:-1], + "shot_description": shot_description + } + print(clip_data) + +def create_otio_timeline(selection): + process_timeline = None + process_segments = [] + + if len(selection) == 1: + if isinstance(selection[0], flame.PySequence): + process_timeline = selection[0] + else: + for item in selection: + if not isinstance(item, flame.PySegment): + continue + process_segments.append(item) + + if process_timeline: + print("___________________timeline__________________") + frame_rate = float(str(process_timeline.frame_rate)[:-4]) + print(frame_rate) + for ver in process_timeline.versions: + for tracks in ver.tracks: + for segment in tracks.segments: + # process all segments + get_segment_attributes(segment, frame_rate) + + if process_segments: + print("___________________segments__________________") + # get segments sequence parent + track = process_segments[0].parent + version = track.parent + flame_timeline = version.parent + frame_rate = float(str(flame_timeline.frame_rate)[:-4]) + + for segment in process_segments: + get_segment_attributes(segment, frame_rate) - # convert track to otio - otio_track = create_otio_track( - type(track), track.name()) - - for itemindex, track_item in enumerate(track): - # Add Gap if needed - if itemindex == 0: - # if it is first track item at track then add - # it to previouse item - prev_item = track_item - - else: - # get previouse item - prev_item = track_item.parent().items()[itemindex - 1] - - # calculate clip frame range difference from each other - clip_diff = track_item.timelineIn() - prev_item.timelineOut() - - # add gap if first track item is not starting - # at first timeline frame - if itemindex == 0 and track_item.timelineIn() > 0: - add_otio_gap(track_item, otio_track, 0) - # or add gap if following track items are having - # frame range differences from each other - elif itemindex and clip_diff != 1: - add_otio_gap(track_item, otio_track, prev_item.timelineOut()) - - # create otio clip and add it to track - otio_clip = create_otio_clip(track_item) - otio_track.append(otio_clip) - - # Add tags as markers - if self.include_tags: - create_otio_markers(otio_track, track) - - # add track to otio timeline - otio_timeline.tracks.append(otio_track) - - return otio_timeline + # get current timeline + # self.timeline = hiero.ui.activeSequence() + # self.project_fps = self.timeline.framerate().toFloat() + + # # convert timeline to otio + # otio_timeline = _create_otio_timeline() + + # # loop all defined track types + # for track in self.timeline.items(): + # # skip if track is disabled + # if not track.isEnabled(): + # continue + + # # convert track to otio + # otio_track = create_otio_track( + # type(track), track.name()) + + # for itemindex, track_item in enumerate(track): + # # Add Gap if needed + # if itemindex == 0: + # # if it is first track item at track then add + # # it to previouse item + # prev_item = track_item + + # else: + # # get previouse item + # prev_item = track_item.parent().items()[itemindex - 1] + + # # calculate clip frame range difference from each other + # clip_diff = track_item.timelineIn() - prev_item.timelineOut() + + # # add gap if first track item is not starting + # # at first timeline frame + # if itemindex == 0 and track_item.timelineIn() > 0: + # add_otio_gap(track_item, otio_track, 0) + + # # or add gap if following track items are having + # # frame range differences from each other + # elif itemindex and clip_diff != 1: + # add_otio_gap(track_item, otio_track, prev_item.timelineOut()) + + # # create otio clip and add it to track + # otio_clip = create_otio_clip(track_item) + # otio_track.append(otio_clip) + + # # Add tags as markers + # if self.include_tags: + # create_otio_markers(otio_track, track) + + # # add track to otio timeline + # otio_timeline.tracks.append(otio_track) + + # return otio_timeline def write_to_file(otio_timeline, path): From f1b158e7e55ee9e452c99c16a7e7e800ab80c2ec Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 14 Dec 2021 17:54:41 +0100 Subject: [PATCH 222/492] added new script and executable to be used as mid process for starting applications on linux --- linux_app_launcher.py | 43 +++++++++++++++++++++++++++++++++++++++++++ setup.py | 19 +++++++++++++++++-- 2 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 linux_app_launcher.py diff --git a/linux_app_launcher.py b/linux_app_launcher.py new file mode 100644 index 00000000000..274ef8b82b3 --- /dev/null +++ b/linux_app_launcher.py @@ -0,0 +1,43 @@ +"""Launch process that is not child process of python or OpenPype. + +This is written for linux distributions where process tree may affect what +is when closed or blocked to be closed. +""" + +import os +import sys +import subprocess +import json + + +def main(input_json_path): + """Read launch arguments from json file and launch the process. + + Expected that json contains "args" key with string or list of strings. + + Arguments are converted to string using `list2cmdline`. At the end is added + `&` which will cause that launched process is detached and running as + "background" process. + + ## Notes + @iLLiCiT: This should be possible to do with 'disown' or double forking but + I didn't find a way how to do it properly. Disown didn't work as + expected for me and double forking killed parent process which is + unexpected too. + """ + with open(input_json_path, "r") as stream: + data = json.load(stream) + + args = data["args"] + if isinstance(args, list): + args = subprocess.list2cmdline(args) + + # Run the command as background process + shell_cmd = args + " &" + os.system(shell_cmd) + sys.exit(0) + + +if __name__ == "__main__": + # Expect that last argument is path to a json with launch args information + main(sys.argv[-1]) diff --git a/setup.py b/setup.py index cd3ed4f82cb..de4887ae3c5 100644 --- a/setup.py +++ b/setup.py @@ -3,6 +3,7 @@ import os import sys import re +import platform from pathlib import Path from cx_Freeze import setup, Executable @@ -18,8 +19,13 @@ version_match = re.search(r"(\d+\.\d+.\d+).*", version["__version__"]) __version__ = version_match.group(1) +low_platform_name = platform.system().lower() +IS_WINDOWS = low_platform_name == "windows" +IS_LINUX = low_platform_name == "linux" +IS_MACOS = low_platform_name == "darwin" + base = None -if sys.platform == "win32": +if IS_WINDOWS: base = "Win32GUI" # ----------------------------------------------------------------------- @@ -71,7 +77,7 @@ "README.md" ] -if sys.platform == "win32": +if IS_WINDOWS: install_requires.extend([ # `pywin32` packages "win32ctypes", @@ -103,6 +109,15 @@ Executable("start.py", base=None, target_name="openpype_console", icon=icon_path.as_posix()) ] +if IS_LINUX: + executables.append( + Executable( + "linux_app_launcher.py", + base=None, + target_name="linux_app_launcher", + icon=icon_path.as_posix() + ) + ) setup( name="OpenPype", From 3e096f41bedfff6e8cd39c482ce64aac55da1766 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 14 Dec 2021 18:00:07 +0100 Subject: [PATCH 223/492] added helper function to return linux app launcher arguments --- openpype/lib/__init__.py | 2 ++ openpype/lib/execute.py | 46 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/openpype/lib/__init__.py b/openpype/lib/__init__.py index efd2cddf7e1..936ee9ea8db 100644 --- a/openpype/lib/__init__.py +++ b/openpype/lib/__init__.py @@ -25,6 +25,7 @@ from .terminal import Terminal from .execute import ( get_pype_execute_args, + get_linux_launcher_args, execute, run_subprocess, path_to_subprocess_arg, @@ -174,6 +175,7 @@ __all__ = [ "get_pype_execute_args", + "get_linux_launcher_args", "execute", "run_subprocess", "path_to_subprocess_arg", diff --git a/openpype/lib/execute.py b/openpype/lib/execute.py index ad77b2f8992..a36a1f0bf48 100644 --- a/openpype/lib/execute.py +++ b/openpype/lib/execute.py @@ -1,7 +1,6 @@ import os -import shlex import subprocess -import platform +import distutils.spawn from .log import PypeLogger as Logger @@ -175,3 +174,46 @@ def get_pype_execute_args(*args): pype_args.extend(args) return pype_args + + +def get_linux_launcher_args(*args): + """Path to application mid process executable. + + This function should be able as arguments are different when used + from code and build. + + It is possible that this function is used in OpenPype build which does + not have yet the new executable. In that case 'None' is returned. + + Args: + args (iterable): List of additional arguments added after executable + argument. + + Returns: + list: Executables with possible positional argument to script when + called from code. + """ + filename = "linux_app_launcher" + openpype_executable = os.environ["OPENPYPE_EXECUTABLE"] + + executable_filename = os.path.basename(openpype_executable) + if "python" in executable_filename.lower(): + script_path = os.path.join( + os.environ["OPENPYPE_ROOT"], + "{}.py".format(filename) + ) + launch_args = [openpype_executable, script_path] + else: + new_executable = os.path.join( + os.path.dirname(openpype_executable), + filename + ) + executable_path = distutils.spawn.find_executable(new_executable) + if executable_path is None: + return None + launch_args = [executable_path] + + if args: + launch_args.extend(args) + + return launch_args From 731a9f4dd961022fbb7633abc3a6437acda4b01c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 14 Dec 2021 18:00:49 +0100 Subject: [PATCH 224/492] flame extract otio wip --- openpype/hosts/flame/otio/flame_export.py | 80 ++++++++++--------- openpype/hosts/flame/otio/utils.py | 2 +- .../plugins/publish/collect_test_selection.py | 5 +- 3 files changed, 46 insertions(+), 41 deletions(-) diff --git a/openpype/hosts/flame/otio/flame_export.py b/openpype/hosts/flame/otio/flame_export.py index 13199fe6c72..818cd4586fa 100644 --- a/openpype/hosts/flame/otio/flame_export.py +++ b/openpype/hosts/flame/otio/flame_export.py @@ -5,10 +5,15 @@ import re import sys import ast - +import logging import opentimelineio as otio from . import utils import flame +from pprint import pformat + +reload(utils) + +log = logging.getLogger(__name__) self = sys.modules[__name__] # self.track_types = { @@ -375,70 +380,67 @@ def add_otio_metadata(otio_item, media_source, **kwargs): otio_item.metadata.update({key: value}) def get_segment_attributes(segment, frame_rate): - print(segment.attributes) + log.info(segment) + # log.info(dir(segment)) + # log.info(segment.attributes) + # track = segment.parent + # log.info("track: {}".format(track)) + # log.info(dir(track)) + # log.info(track.attributes) + # log.info(track.name) + if str(segment.name)[1:-1] == "": return None - # get clip frame duration - record_duration = str(segment.record_duration)[1:-1] - clip_duration = utils.timecode_to_frames( - record_duration, frame_rate) + # Add timeline segment to tree + clip_data = { + "clip_name": str(segment.name)[1:-1], + "clip_comment": str(segment.comment)[1:-1], + "tape_name": str(segment.tape_name), + "source_name": str(segment.source_name), + "file_path": str(segment.file_path) + } # populate shot source metadata - shot_description = "" - for attr in ["tape_name", "source_name", "head", - "tail", "file_path"]: + segment_attrs = [ + "record_duration", "record_in", "record_out", + "source_duration", "source_in", "source_out" + ] + segment_attrs_data = {} + for attr in segment_attrs: if not hasattr(segment, attr): continue _value = getattr(segment, attr) - _label = attr.replace("_", " ").capitalize() - row = "{}: {}\n".format(_label, _value) - shot_description += row + segment_attrs_data[attr] = _value + _value = str(_value)[1:-1] + clip_data[attr] = utils.timecode_to_frames( + _value, frame_rate) - # Add timeline segment to tree - clip_data = { - "clip_name": str(segment.name)[1:-1], - "clip_duration": str(clip_duration), - "clip_comment": str(segment.comment)[1:-1], - "shot_description": shot_description - } - print(clip_data) + clip_data["segment_attrs"] = segment_attrs_data + + log.info(pformat(clip_data)) def create_otio_timeline(selection): process_timeline = None - process_segments = [] if len(selection) == 1: if isinstance(selection[0], flame.PySequence): process_timeline = selection[0] else: - for item in selection: - if not isinstance(item, flame.PySegment): - continue - process_segments.append(item) + track = selection[0].parent + version = track.parent + process_timeline = version.parent if process_timeline: - print("___________________timeline__________________") + log.info("___________________timeline__________________") frame_rate = float(str(process_timeline.frame_rate)[:-4]) - print(frame_rate) + log.info(frame_rate) for ver in process_timeline.versions: for tracks in ver.tracks: for segment in tracks.segments: # process all segments get_segment_attributes(segment, frame_rate) - if process_segments: - print("___________________segments__________________") - # get segments sequence parent - track = process_segments[0].parent - version = track.parent - flame_timeline = version.parent - frame_rate = float(str(flame_timeline.frame_rate)[:-4]) - - for segment in process_segments: - get_segment_attributes(segment, frame_rate) - - # get current timeline # self.timeline = hiero.ui.activeSequence() # self.project_fps = self.timeline.framerate().toFloat() diff --git a/openpype/hosts/flame/otio/utils.py b/openpype/hosts/flame/otio/utils.py index 4c5d46bd51d..2d927957c97 100644 --- a/openpype/hosts/flame/otio/utils.py +++ b/openpype/hosts/flame/otio/utils.py @@ -3,7 +3,7 @@ def timecode_to_frames(timecode, framerate): - rt = otio.opentime.from_timecode(timecode, 24) + rt = otio.opentime.from_timecode(timecode, framerate) return int(otio.opentime.to_frames(rt)) diff --git a/openpype/hosts/flame/plugins/publish/collect_test_selection.py b/openpype/hosts/flame/plugins/publish/collect_test_selection.py index 6534894f2e1..dbbbc2f0bd9 100644 --- a/openpype/hosts/flame/plugins/publish/collect_test_selection.py +++ b/openpype/hosts/flame/plugins/publish/collect_test_selection.py @@ -1,6 +1,8 @@ import pyblish.api import openpype.hosts.flame as opflame +from openpype.hosts.flame.otio import flame_export as otio_export import flame +reload(otio_export) @pyblish.api.log class CollectTestSelection(pyblish.api.ContextPlugin): @@ -12,4 +14,5 @@ class CollectTestSelection(pyblish.api.ContextPlugin): hosts = ["flame"] def process(self, context): - self.log.info(opflame.selection) \ No newline at end of file + self.log.info(opflame.selection) + otio_export.create_otio_timeline(opflame.selection) \ No newline at end of file From 0b9f9450f6e4a9a63543c491e4a18443a4aebdc2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 14 Dec 2021 18:02:29 +0100 Subject: [PATCH 225/492] use new function on application launch --- openpype/lib/applications.py | 47 ++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/openpype/lib/applications.py b/openpype/lib/applications.py index 6eb44a9694e..97cc2136466 100644 --- a/openpype/lib/applications.py +++ b/openpype/lib/applications.py @@ -1,8 +1,8 @@ import os import sys -import re import copy import json +import tempfile import platform import collections import inspect @@ -37,6 +37,7 @@ modules_from_path, classes_from_module ) +from .execute import get_linux_launcher_args _logger = None @@ -921,6 +922,46 @@ def app_group(self): def manager(self): return self.application.manager + def _run_process(self): + # Windows and MacOS have easier process start + low_platform = platform.system().lower() + if low_platform in ("windows", "darwin"): + return subprocess.Popen(self.launch_args, **self.kwargs) + + # Linux uses mid process + # - it is possible that the mid process executable is not + # available for this version of OpenPype in that case use standard + # launch + launch_args = get_linux_launcher_args() + if launch_args is None: + return subprocess.Popen(self.launch_args, **self.kwargs) + + # Prepare data that will be passed to midprocess + # - store arguments to a json and pass path to json as last argument + json_data = { + "args": self.launch_args + } + # Create temp file + json_temp = tempfile.NamedTemporaryFile( + mode="w", prefix="op_app_args", suffix=".json", delete=False + ) + json_temp.close() + json_temp_filpath = json_temp.name + with open(json_temp_filpath, "w") as stream: + json.dump(json_data, stream) + + launch_args.append(json_temp_filpath) + + # Create mid-process which will launch application + process = subprocess.Popen(launch_args, **self.kwargs) + # Wait until the process finishes + # - This is important! The process would stay in "open" state. + process.wait() + # Remove the temp file + os.remove(json_temp_filpath) + # Return process which is already terminated + return process + def launch(self): """Collect data for new process and then create it. @@ -957,8 +998,10 @@ def launch(self): self.app_name, args_len_str, args ) ) + self.launch_args = args + # Run process - self.process = subprocess.Popen(args, **self.kwargs) + self.process = self._run_process() # Process post launch hooks for postlaunch_hook in self.postlaunch_hooks: From 512bf2e3f66c2523eedd52f2434ea2b84b5772be Mon Sep 17 00:00:00 2001 From: OpenPype Date: Wed, 15 Dec 2021 03:41:30 +0000 Subject: [PATCH 226/492] [Automated] Bump version --- CHANGELOG.md | 54 ++++++++++++++------------------------------- openpype/version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 18 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 860e26c59ca..dde81386295 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,12 @@ # Changelog -## [3.7.0-nightly.7](https://github.com/pypeclub/OpenPype/tree/HEAD) +## [3.7.0-nightly.8](https://github.com/pypeclub/OpenPype/tree/HEAD) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.6.4...HEAD) -### 📖 Documentation +**Deprecated:** -- docs\[website\]: Add Ellipse Studio \(logo\) as an OpenPype contributor [\#2324](https://github.com/pypeclub/OpenPype/pull/2324) +- General: Default modules hierarchy n2 [\#2368](https://github.com/pypeclub/OpenPype/pull/2368) **🆕 New features** @@ -14,27 +14,31 @@ **🚀 Enhancements** +- Ftrack: Check existence of object type on recreation [\#2404](https://github.com/pypeclub/OpenPype/pull/2404) +- Flame: moving `utility\_scripts` to api folder also with `scripts` [\#2385](https://github.com/pypeclub/OpenPype/pull/2385) +- Centos 7 dependency compatibility [\#2384](https://github.com/pypeclub/OpenPype/pull/2384) +- Enhancement: Settings: Use project settings values from another project [\#2382](https://github.com/pypeclub/OpenPype/pull/2382) +- Blender 3: Support auto install for new blender version [\#2377](https://github.com/pypeclub/OpenPype/pull/2377) +- Maya add render image path to settings [\#2375](https://github.com/pypeclub/OpenPype/pull/2375) - Settings: Webpublisher in hosts enum [\#2367](https://github.com/pypeclub/OpenPype/pull/2367) - Hiero: python3 compatibility [\#2365](https://github.com/pypeclub/OpenPype/pull/2365) - Burnins: Be able recognize mxf OPAtom format [\#2361](https://github.com/pypeclub/OpenPype/pull/2361) +- Maya: Add is\_static\_image\_plane and is\_in\_all\_views option in imagePlaneLoader [\#2356](https://github.com/pypeclub/OpenPype/pull/2356) - Local settings: Copyable studio paths [\#2349](https://github.com/pypeclub/OpenPype/pull/2349) - Assets Widget: Clear model on project change [\#2345](https://github.com/pypeclub/OpenPype/pull/2345) - General: OpenPype default modules hierarchy [\#2338](https://github.com/pypeclub/OpenPype/pull/2338) - General: FFprobe error exception contain original error message [\#2328](https://github.com/pypeclub/OpenPype/pull/2328) - Resolve: Add experimental button to menu [\#2325](https://github.com/pypeclub/OpenPype/pull/2325) -- Hiero: Add experimental tools action [\#2323](https://github.com/pypeclub/OpenPype/pull/2323) - Input links: Cleanup and unification of differences [\#2322](https://github.com/pypeclub/OpenPype/pull/2322) - General: Don't validate vendor bin with executing them [\#2317](https://github.com/pypeclub/OpenPype/pull/2317) - General: Multilayer EXRs support [\#2315](https://github.com/pypeclub/OpenPype/pull/2315) -- General: Run process log stderr as info log level [\#2309](https://github.com/pypeclub/OpenPype/pull/2309) - General: Reduce vendor imports [\#2305](https://github.com/pypeclub/OpenPype/pull/2305) -- Tools: Cleanup of unused classes [\#2304](https://github.com/pypeclub/OpenPype/pull/2304) -- Project Manager: Added ability to delete project [\#2298](https://github.com/pypeclub/OpenPype/pull/2298) -- Ftrack: Synchronize input links [\#2287](https://github.com/pypeclub/OpenPype/pull/2287) -- Nuke: extract baked review videos presets [\#2248](https://github.com/pypeclub/OpenPype/pull/2248) **🐛 Bug fixes** +- General: Fix access to environments from default settings [\#2403](https://github.com/pypeclub/OpenPype/pull/2403) +- Fix: Placeholder Input color set fix [\#2399](https://github.com/pypeclub/OpenPype/pull/2399) +- Settings: Fix state change of wrapper label [\#2396](https://github.com/pypeclub/OpenPype/pull/2396) - Flame: fix ftrack publisher [\#2381](https://github.com/pypeclub/OpenPype/pull/2381) - hiero: solve custom ocio path [\#2379](https://github.com/pypeclub/OpenPype/pull/2379) - hiero: fix workio and flatten [\#2378](https://github.com/pypeclub/OpenPype/pull/2378) @@ -53,21 +57,16 @@ - Timers Manager: Disable auto stop timer on linux platform [\#2334](https://github.com/pypeclub/OpenPype/pull/2334) - nuke: bake preset single input exception [\#2331](https://github.com/pypeclub/OpenPype/pull/2331) - Hiero: fixing multiple templates at a hierarchy parent [\#2330](https://github.com/pypeclub/OpenPype/pull/2330) -- Fix - provider icons are pulled from a folder [\#2326](https://github.com/pypeclub/OpenPype/pull/2326) -- InputLinks: Typo in "inputLinks" key [\#2314](https://github.com/pypeclub/OpenPype/pull/2314) -- Deadline timeout and logging [\#2312](https://github.com/pypeclub/OpenPype/pull/2312) -- nuke: do not multiply representation on class method [\#2311](https://github.com/pypeclub/OpenPype/pull/2311) -- Workfiles tool: Fix task formatting [\#2306](https://github.com/pypeclub/OpenPype/pull/2306) -- Delivery: Fix delivery paths created on windows [\#2302](https://github.com/pypeclub/OpenPype/pull/2302) -- Maya: Deadline - fix limit groups [\#2295](https://github.com/pypeclub/OpenPype/pull/2295) - Royal Render: Fix plugin order and OpenPype auto-detection [\#2291](https://github.com/pypeclub/OpenPype/pull/2291) -- Alternate site for site sync doesnt work for sequences [\#2284](https://github.com/pypeclub/OpenPype/pull/2284) **Merged pull requests:** +- \[Fix\]\[MAYA\] Handle message type attribute within CollectLook [\#2394](https://github.com/pypeclub/OpenPype/pull/2394) +- Add validator to check correct version of extension for PS and AE [\#2387](https://github.com/pypeclub/OpenPype/pull/2387) - Linux : flip updating submodules logic [\#2357](https://github.com/pypeclub/OpenPype/pull/2357) - Update of avalon-core [\#2346](https://github.com/pypeclub/OpenPype/pull/2346) - Maya: configurable model top level validation [\#2321](https://github.com/pypeclub/OpenPype/pull/2321) +- Create test publish class for After Effects [\#2270](https://github.com/pypeclub/OpenPype/pull/2270) ## [3.6.4](https://github.com/pypeclub/OpenPype/tree/3.6.4) (2021-11-23) @@ -93,42 +92,21 @@ - Tools: Assets widget [\#2265](https://github.com/pypeclub/OpenPype/pull/2265) - SceneInventory: Choose loader in asset switcher [\#2262](https://github.com/pypeclub/OpenPype/pull/2262) -- Style: New fonts in OpenPype style [\#2256](https://github.com/pypeclub/OpenPype/pull/2256) -- Tools: Tasks widget [\#2251](https://github.com/pypeclub/OpenPype/pull/2251) -- Tools: Creator in OpenPype [\#2244](https://github.com/pypeclub/OpenPype/pull/2244) **🐛 Bug fixes** - Tools: Parenting of tools in Nuke and Hiero [\#2266](https://github.com/pypeclub/OpenPype/pull/2266) - limiting validator to specific editorial hosts [\#2264](https://github.com/pypeclub/OpenPype/pull/2264) - Tools: Select Context dialog attribute fix [\#2261](https://github.com/pypeclub/OpenPype/pull/2261) -- Maya: Render publishing fails on linux [\#2260](https://github.com/pypeclub/OpenPype/pull/2260) -- LookAssigner: Fix tool reopen [\#2259](https://github.com/pypeclub/OpenPype/pull/2259) -- Standalone: editorial not publishing thumbnails on all subsets [\#2258](https://github.com/pypeclub/OpenPype/pull/2258) -- Burnins: Support mxf metadata [\#2247](https://github.com/pypeclub/OpenPype/pull/2247) ## [3.6.1](https://github.com/pypeclub/OpenPype/tree/3.6.1) (2021-11-16) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.6.1-nightly.1...3.6.1) -**🐛 Bug fixes** - -- Loader doesn't allow changing of version before loading [\#2254](https://github.com/pypeclub/OpenPype/pull/2254) - ## [3.6.0](https://github.com/pypeclub/OpenPype/tree/3.6.0) (2021-11-15) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.6.0-nightly.6...3.6.0) -**🚀 Enhancements** - -- Tools: SceneInventory in OpenPype [\#2255](https://github.com/pypeclub/OpenPype/pull/2255) -- Tools: Subset manager in OpenPype [\#2243](https://github.com/pypeclub/OpenPype/pull/2243) - -**🐛 Bug fixes** - -- Ftrack: Sync project ftrack id cache issue [\#2250](https://github.com/pypeclub/OpenPype/pull/2250) -- Ftrack: Session creation and Prepare project [\#2245](https://github.com/pypeclub/OpenPype/pull/2245) - ## [3.5.0](https://github.com/pypeclub/OpenPype/tree/3.5.0) (2021-10-17) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.5.0-nightly.8...3.5.0) diff --git a/openpype/version.py b/openpype/version.py index 95cd7a285f1..06bc20ae433 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.7.0-nightly.7" +__version__ = "3.7.0-nightly.8" diff --git a/pyproject.toml b/pyproject.toml index d994569fa74..e5d552bb3b6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.7.0-nightly.7" # OpenPype +version = "3.7.0-nightly.8" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From cab1f32a5ac4e1d5dac51f4e47f419b2381b33ae Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 15 Dec 2021 10:41:14 +0100 Subject: [PATCH 227/492] abstracting get timeline to lib --- openpype/hosts/flame/__init__.py | 4 +-- openpype/hosts/flame/api/lib.py | 26 +++++++++++++---- openpype/hosts/flame/otio/flame_export.py | 29 ++++++------------- .../plugins/publish/collect_test_selection.py | 8 +++-- 4 files changed, 38 insertions(+), 29 deletions(-) diff --git a/openpype/hosts/flame/__init__.py b/openpype/hosts/flame/__init__.py index f04b73baba8..da281706798 100644 --- a/openpype/hosts/flame/__init__.py +++ b/openpype/hosts/flame/__init__.py @@ -19,7 +19,7 @@ maintain_current_timeline, get_project_manager, get_current_project, - get_current_timeline, + get_current_sequence, create_bin, ) @@ -88,7 +88,7 @@ "maintain_current_timeline", "get_project_manager", "get_current_project", - "get_current_timeline", + "get_current_sequence", "create_bin", # menu diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index 89e020b329d..941f6b7d758 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -220,10 +220,10 @@ def maintain_current_timeline(to_timeline, from_timeline=None): timeline2 >>> with maintain_current_timeline(to_timeline): - ... print(get_current_timeline().GetName()) + ... print(get_current_sequence().GetName()) timeline2 - >>> print(get_current_timeline().GetName()) + >>> print(get_current_sequence().GetName()) timeline1 """ # todo: this is still Resolve's implementation @@ -256,9 +256,25 @@ def get_current_project(): return -def get_current_timeline(new=False): - # TODO: get_current_timeline - return +def get_current_sequence(selection): + import flame + + process_timeline = None + + if len(selection) == 1: + if isinstance(selection[0], flame.PySequence): + process_timeline = selection[0] + else: + process_segment = None + for segment in selection: + if isinstance(segment, flame.PySegment): + process_segment = segment + break + track = process_segment.parent + version = track.parent + process_timeline = version.parent + + return process_timeline def create_bin(name, root=None): diff --git a/openpype/hosts/flame/otio/flame_export.py b/openpype/hosts/flame/otio/flame_export.py index 818cd4586fa..9fb219a5450 100644 --- a/openpype/hosts/flame/otio/flame_export.py +++ b/openpype/hosts/flame/otio/flame_export.py @@ -8,6 +8,7 @@ import logging import opentimelineio as otio from . import utils + import flame from pprint import pformat @@ -420,26 +421,14 @@ def get_segment_attributes(segment, frame_rate): log.info(pformat(clip_data)) -def create_otio_timeline(selection): - process_timeline = None - - if len(selection) == 1: - if isinstance(selection[0], flame.PySequence): - process_timeline = selection[0] - else: - track = selection[0].parent - version = track.parent - process_timeline = version.parent - - if process_timeline: - log.info("___________________timeline__________________") - frame_rate = float(str(process_timeline.frame_rate)[:-4]) - log.info(frame_rate) - for ver in process_timeline.versions: - for tracks in ver.tracks: - for segment in tracks.segments: - # process all segments - get_segment_attributes(segment, frame_rate) +def create_otio_timeline(sequence): + frame_rate = float(str(sequence.frame_rate)[:-4]) + log.info(frame_rate) + for ver in sequence.versions: + for tracks in ver.tracks: + for segment in tracks.segments: + # process all segments + get_segment_attributes(segment, frame_rate) # get current timeline # self.timeline = hiero.ui.activeSequence() diff --git a/openpype/hosts/flame/plugins/publish/collect_test_selection.py b/openpype/hosts/flame/plugins/publish/collect_test_selection.py index dbbbc2f0bd9..cefd9ee7cfc 100644 --- a/openpype/hosts/flame/plugins/publish/collect_test_selection.py +++ b/openpype/hosts/flame/plugins/publish/collect_test_selection.py @@ -1,7 +1,7 @@ import pyblish.api import openpype.hosts.flame as opflame from openpype.hosts.flame.otio import flame_export as otio_export -import flame +from pprint import pformat reload(otio_export) @pyblish.api.log @@ -15,4 +15,8 @@ class CollectTestSelection(pyblish.api.ContextPlugin): def process(self, context): self.log.info(opflame.selection) - otio_export.create_otio_timeline(opflame.selection) \ No newline at end of file + + sequence = opflame.get_current_sequence(opflame.selection) + otio_timeline = otio_export.create_otio_timeline(sequence) + + self.log.info(pformat(otio_timeline)) \ No newline at end of file From 1ffdf801cc51e7da1eae496cff1342ddb1f99def Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 15 Dec 2021 14:52:04 +0100 Subject: [PATCH 228/492] otio export modul wip --- openpype/hosts/flame/otio/flame_export.py | 122 ++++++++++++++-------- 1 file changed, 79 insertions(+), 43 deletions(-) diff --git a/openpype/hosts/flame/otio/flame_export.py b/openpype/hosts/flame/otio/flame_export.py index 9fb219a5450..e7735be701c 100644 --- a/openpype/hosts/flame/otio/flame_export.py +++ b/openpype/hosts/flame/otio/flame_export.py @@ -21,7 +21,7 @@ # hiero.core.VideoTrack: otio.schema.TrackKind.Video, # hiero.core.AudioTrack: otio.schema.TrackKind.Audio # } -self.project_fps = None +self.fps = None self.marker_color_map = { "magenta": otio.schema.MarkerColor.MAGENTA, "red": otio.schema.MarkerColor.RED, @@ -30,7 +30,6 @@ "cyan": otio.schema.MarkerColor.CYAN, "blue": otio.schema.MarkerColor.BLUE, } -self.timeline = None self.include_tags = True @@ -151,7 +150,7 @@ def create_otio_reference(clip): file_head = media_source.filenameHead() is_sequence = not media_source.singleFile() frame_duration = media_source.duration() - fps = utils.get_rate(clip) or self.project_fps + fps = utils.get_rate(clip) or self.fps extension = os.path.splitext(path)[-1] if is_sequence: @@ -231,7 +230,7 @@ def create_otio_markers(otio_item, item): # Hiero adds this tag to a lot of clips continue - frame_rate = utils.get_rate(item) or self.project_fps + frame_rate = utils.get_rate(item) or self.fps marked_range = otio.opentime.TimeRange( start_time=otio.opentime.RationalTime( @@ -278,7 +277,7 @@ def create_otio_clip(track_item): duration = int(track_item.duration()) - fps = utils.get_rate(track_item) or self.project_fps + fps = utils.get_rate(track_item) or self.fps name = track_item.name() media_reference = create_otio_reference(clip) @@ -317,28 +316,28 @@ def create_otio_gap(gap_start, clip_start, tl_start_frame, fps): ) -def _create_otio_timeline(): +def _create_otio_timeline(sequence): project = get_current_flame_project() - metadata = _get_metadata(self.timeline) + metadata = _get_metadata(sequence) metadata.update({ - "openpype.timeline.width": int(self.timeline.format().width()), - "openpype.timeline.height": int(self.timeline.format().height()), - "openpype.timeline.pixelAspect": int(self.timeline.format().pixelAspect()), # noqa - "openpype.project.useOCIOEnvironmentOverride": project.useOCIOEnvironmentOverride(), # noqa - "openpype.project.lutSetting16Bit": project.lutSetting16Bit(), - "openpype.project.lutSetting8Bit": project.lutSetting8Bit(), - "openpype.project.lutSettingFloat": project.lutSettingFloat(), - "openpype.project.lutSettingLog": project.lutSettingLog(), - "openpype.project.lutSettingViewer": project.lutSettingViewer(), - "openpype.project.lutSettingWorkingSpace": project.lutSettingWorkingSpace(), # noqa - "openpype.project.lutUseOCIOForExport": project.lutUseOCIOForExport(), - "openpype.project.ocioConfigName": project.ocioConfigName(), - "openpype.project.ocioConfigPath": project.ocioConfigPath() + "openpype.timeline.width": int(sequence.width), + "openpype.timeline.height": int(sequence.height), + "openpype.timeline.pixelAspect": 1, # noqa + # "openpype.project.useOCIOEnvironmentOverride": project.useOCIOEnvironmentOverride(), # noqa + # "openpype.project.lutSetting16Bit": project.lutSetting16Bit(), + # "openpype.project.lutSetting8Bit": project.lutSetting8Bit(), + # "openpype.project.lutSettingFloat": project.lutSettingFloat(), + # "openpype.project.lutSettingLog": project.lutSettingLog(), + # "openpype.project.lutSettingViewer": project.lutSettingViewer(), + # "openpype.project.lutSettingWorkingSpace": project.lutSettingWorkingSpace(), # noqa + # "openpype.project.lutUseOCIOForExport": project.lutUseOCIOForExport(), + # "openpype.project.ocioConfigName": project.ocioConfigName(), + # "openpype.project.ocioConfigPath": project.ocioConfigPath() }) start_time = create_otio_rational_time( - self.timeline.timecodeStart(), self.project_fps) + self.timeline.timecodeStart(), self.fps) return otio.schema.Timeline( name=self.timeline.name(), @@ -362,7 +361,7 @@ def add_otio_gap(track_item, otio_track, prev_out): gap = otio.opentime.TimeRange( duration=otio.opentime.RationalTime( gap_length, - self.project_fps + self.fps ) ) otio_gap = otio.schema.Gap(source_range=gap) @@ -382,24 +381,17 @@ def add_otio_metadata(otio_item, media_source, **kwargs): def get_segment_attributes(segment, frame_rate): log.info(segment) - # log.info(dir(segment)) - # log.info(segment.attributes) - # track = segment.parent - # log.info("track: {}".format(track)) - # log.info(dir(track)) - # log.info(track.attributes) - # log.info(track.name) if str(segment.name)[1:-1] == "": return None # Add timeline segment to tree clip_data = { - "clip_name": str(segment.name)[1:-1], - "clip_comment": str(segment.comment)[1:-1], + "name": str(segment.name)[1:-1], + "comment": str(segment.comment)[1:-1], "tape_name": str(segment.tape_name), "source_name": str(segment.source_name), - "file_path": str(segment.file_path) + "fpath": str(segment.file_path) } # populate shot source metadata @@ -417,25 +409,69 @@ def get_segment_attributes(segment, frame_rate): clip_data[attr] = utils.timecode_to_frames( _value, frame_rate) - clip_data["segment_attrs"] = segment_attrs_data + clip_data["segment_timecodes"] = segment_attrs_data log.info(pformat(clip_data)) + return clip_data + +def get_track_attributes(track, frame_rate): + log.info(track) + log.info(dir(track)) + log.info(track.attributes) + + if len(track.segments) == 0: + return None + + # Add timeline segment to tree + track_data = { + "name": str(track.name)[1:-1] + } + + # # populate shot source metadata + # segment_attrs = [ + # "record_duration", "record_in", "record_out", + # "source_duration", "source_in", "source_out" + # ] + # segment_attrs_data = {} + # for attr in segment_attrs: + # if not hasattr(segment, attr): + # continue + # _value = getattr(segment, attr) + # segment_attrs_data[attr] = _value + # _value = str(_value)[1:-1] + # clip_data[attr] = utils.timecode_to_frames( + # _value, frame_rate) + + # clip_data["segment_timecodes"] = segment_attrs_data + + log.info(pformat(track_data)) + return track_data def create_otio_timeline(sequence): - frame_rate = float(str(sequence.frame_rate)[:-4]) - log.info(frame_rate) - for ver in sequence.versions: - for tracks in ver.tracks: - for segment in tracks.segments: - # process all segments - get_segment_attributes(segment, frame_rate) + log.info(dir(sequence)) + log.info(sequence.attributes) # get current timeline - # self.timeline = hiero.ui.activeSequence() - # self.project_fps = self.timeline.framerate().toFloat() + self.fps = float(str(sequence.frame_rate)[:-4]) # # convert timeline to otio - # otio_timeline = _create_otio_timeline() + otio_timeline = _create_otio_timeline(sequence) + + # create otio tracks and clips + for ver in sequence.versions: + for track in ver.tracks: + track_data = get_track_attributes(track, self.fps) + if not track_data: + continue + for segment in track.segments: + # process all segments + clip_data = get_segment_attributes( + segment, self.fps) + # create otio clip + # create otio reference + # create otio marker + # create otio metadata + # # loop all defined track types # for track in self.timeline.items(): From bda4a3c934b6947bf3a15cc7adfd2f5c7cd7a783 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20LORRAIN?= Date: Wed, 15 Dec 2021 15:47:33 +0100 Subject: [PATCH 229/492] Fix build regex and improve filter for create_zip cache cleaning --- tools/create_zip.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/create_zip.ps1 b/tools/create_zip.ps1 index c27857b4804..99e4b395508 100644 --- a/tools/create_zip.ps1 +++ b/tools/create_zip.ps1 @@ -96,9 +96,9 @@ if (-not (Test-Path -PathType Container -Path "$($env:POETRY_HOME)\bin")) { Write-Host ">>> " -NoNewline -ForegroundColor green Write-Host "Cleaning cache files ... " -NoNewline -Get-ChildItem $openpype_root -Filter "*.pyc" -Force -Recurse | Where-Object { $_.FullName -inotmatch 'build' } | Remove-Item -Force -Get-ChildItem $openpype_root -Filter "*.pyo" -Force -Recurse | Where-Object { $_.FullName -inotmatch 'build' } | Remove-Item -Force -Get-ChildItem $openpype_root -Filter "__pycache__" -Force -Recurse| Where-Object { $_.FullName -inotmatch 'build' } | Remove-Item -Force -Recurse +Get-ChildItem $openpype_root -Filter "__pycache__" -Force -Recurse| Where-Object {( $_.FullName -inotmatch '\\build\\' ) -and ( $_.FullName -inotmatch '\\.venv' )} | Remove-Item -Force -Recurse +Get-ChildItem $openpype_root -Filter "*.pyc" -Force -Recurse | Where-Object {( $_.FullName -inotmatch '\\build\\' ) -and ( $_.FullName -inotmatch '\.venv' )} | Remove-Item -Force +Get-ChildItem $openpype_root -Filter "*.pyo" -Force -Recurse | Where-Object {( $_.FullName -inotmatch '\\build\\' ) -and ( $_.FullName -inotmatch '\.venv' )} | Remove-Item -Force Write-Host "OK" -ForegroundColor green Write-Host ">>> " -NoNewline -ForegroundColor green From 654aedbca1cdff4ac39a4c33e2cfbdc4f47753ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20LORRAIN?= Date: Wed, 15 Dec 2021 16:13:26 +0100 Subject: [PATCH 230/492] fix regex typo --- tools/create_zip.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/create_zip.ps1 b/tools/create_zip.ps1 index 99e4b395508..e33445d1fa1 100644 --- a/tools/create_zip.ps1 +++ b/tools/create_zip.ps1 @@ -97,8 +97,8 @@ if (-not (Test-Path -PathType Container -Path "$($env:POETRY_HOME)\bin")) { Write-Host ">>> " -NoNewline -ForegroundColor green Write-Host "Cleaning cache files ... " -NoNewline Get-ChildItem $openpype_root -Filter "__pycache__" -Force -Recurse| Where-Object {( $_.FullName -inotmatch '\\build\\' ) -and ( $_.FullName -inotmatch '\\.venv' )} | Remove-Item -Force -Recurse -Get-ChildItem $openpype_root -Filter "*.pyc" -Force -Recurse | Where-Object {( $_.FullName -inotmatch '\\build\\' ) -and ( $_.FullName -inotmatch '\.venv' )} | Remove-Item -Force -Get-ChildItem $openpype_root -Filter "*.pyo" -Force -Recurse | Where-Object {( $_.FullName -inotmatch '\\build\\' ) -and ( $_.FullName -inotmatch '\.venv' )} | Remove-Item -Force +Get-ChildItem $openpype_root -Filter "*.pyc" -Force -Recurse | Where-Object {( $_.FullName -inotmatch '\\build\\' ) -and ( $_.FullName -inotmatch '\\.venv' )} | Remove-Item -Force +Get-ChildItem $openpype_root -Filter "*.pyo" -Force -Recurse | Where-Object {( $_.FullName -inotmatch '\\build\\' ) -and ( $_.FullName -inotmatch '\\.venv' )} | Remove-Item -Force Write-Host "OK" -ForegroundColor green Write-Host ">>> " -NoNewline -ForegroundColor green From 6909db9d7ca3c1ff057c8bf4ec6ff950ca2361fc Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 15 Dec 2021 23:04:51 +0100 Subject: [PATCH 231/492] otio export module wip --- openpype/hosts/flame/api/lib.py | 12 +- openpype/hosts/flame/otio/flame_export.py | 241 +++++++++------------- openpype/hosts/flame/otio/utils.py | 46 ++--- 3 files changed, 121 insertions(+), 178 deletions(-) diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index 941f6b7d758..fba2d8f5c84 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -258,21 +258,23 @@ def get_current_project(): def get_current_sequence(selection): import flame + def segment_to_sequence(_segment): + track = _segment.parent + version = track.parent + return version.parent process_timeline = None if len(selection) == 1: if isinstance(selection[0], flame.PySequence): process_timeline = selection[0] + if isinstance(selection[0], flame.PySegment): + process_timeline = segment_to_sequence(selection[0]) else: - process_segment = None for segment in selection: if isinstance(segment, flame.PySegment): - process_segment = segment + process_timeline = segment_to_sequence(segment) break - track = process_segment.parent - version = track.parent - process_timeline = version.parent return process_timeline diff --git a/openpype/hosts/flame/otio/flame_export.py b/openpype/hosts/flame/otio/flame_export.py index e7735be701c..e55e72f55e4 100644 --- a/openpype/hosts/flame/otio/flame_export.py +++ b/openpype/hosts/flame/otio/flame_export.py @@ -17,11 +17,13 @@ log = logging.getLogger(__name__) self = sys.modules[__name__] -# self.track_types = { -# hiero.core.VideoTrack: otio.schema.TrackKind.Video, -# hiero.core.AudioTrack: otio.schema.TrackKind.Audio -# } +self.track_types = { + "video": otio.schema.TrackKind.Video, + "audio": otio.schema.TrackKind.Audio +} self.fps = None +self.seq_frame_start = None + self.marker_color_map = { "magenta": otio.schema.MarkerColor.MAGENTA, "red": otio.schema.MarkerColor.RED, @@ -59,10 +61,12 @@ def create_otio_time_range(start_frame, frame_duration, fps): duration=create_otio_rational_time(frame_duration, fps) ) - def _get_metadata(item): if hasattr(item, 'metadata'): - return {key: value for key, value in dict(item.metadata()).items()} + log.debug(item.metadata) + if not item.metadata: + return {} + return {key: value for key, value in dict(item.metadata)} return {} @@ -136,22 +140,23 @@ def create_time_effects(otio_clip, track_item): otio_clip.effects.append(otio_effect) -def create_otio_reference(clip): - metadata = _get_metadata(clip) - media_source = clip.mediaSource() +def create_otio_reference(clip_data): + metadata = _get_metadata(clip_data) # get file info for path and start frame - file_info = media_source.fileinfos().pop() - frame_start = file_info.startFrame() - path = file_info.filename() + frame_start = 0 + path = clip_data["fpath"] + file_name = os.path.basename(path) + file_head, extension = os.path.splitext(file_name) # get padding and other file infos - padding = media_source.filenamePadding() - file_head = media_source.filenameHead() - is_sequence = not media_source.singleFile() - frame_duration = media_source.duration() - fps = utils.get_rate(clip) or self.fps - extension = os.path.splitext(path)[-1] + is_sequence = padding = utils.get_padding_from_path(path) + if is_sequence: + padding_pattern = re.compile(r"[._](\d+)[.]") + number = re.findall(padding_pattern, path).pop() + file_head = file_name.split(number)[:-1] + + frame_duration = clip_data["source_duration"] if is_sequence: metadata.update({ @@ -159,13 +164,6 @@ def create_otio_reference(clip): "padding": padding }) - # add resolution metadata - metadata.update({ - "openpype.source.colourtransform": clip.sourceMediaColourTransform(), - "openpype.source.width": int(media_source.width()), - "openpype.source.height": int(media_source.height()), - "openpype.source.pixelAspect": float(media_source.pixelAspect()) - }) otio_ex_ref_item = None @@ -180,11 +178,11 @@ def create_otio_reference(clip): name_suffix=extension, start_frame=frame_start, frame_zero_padding=padding, - rate=fps, + rate=self.fps, available_range=create_otio_time_range( frame_start, frame_duration, - fps + self.fps ) ) except AttributeError: @@ -198,12 +196,12 @@ def create_otio_reference(clip): available_range=create_otio_time_range( frame_start, frame_duration, - fps + self.fps ) ) # add metadata to otio item - add_otio_metadata(otio_ex_ref_item, media_source, **metadata) + # add_otio_metadata(otio_ex_ref_item, media_source, **metadata) return otio_ex_ref_item @@ -269,26 +267,17 @@ def create_otio_markers(otio_item, item): otio_item.markers.append(marker) -def create_otio_clip(track_item): - clip = track_item.source() - speed = track_item.playbackSpeed() - # flip if speed is in minus - source_in = track_item.sourceIn() if speed > 0 else track_item.sourceOut() - - duration = int(track_item.duration()) - - fps = utils.get_rate(track_item) or self.fps - name = track_item.name() +def create_otio_clip(clip_data): - media_reference = create_otio_reference(clip) + media_reference = create_otio_reference(clip_data) source_range = create_otio_time_range( - int(source_in), - int(duration), - fps + clip_data["source_in"], + clip_data["record_duration"], + self.fps ) otio_clip = otio.schema.Clip( - name=name, + name=clip_data["name"], source_range=source_range, media_reference=media_reference ) @@ -336,12 +325,12 @@ def _create_otio_timeline(sequence): # "openpype.project.ocioConfigPath": project.ocioConfigPath() }) - start_time = create_otio_rational_time( - self.timeline.timecodeStart(), self.fps) + rt_start_time = create_otio_rational_time( + self.seq_frame_start, self.fps) return otio.schema.Timeline( - name=self.timeline.name(), - global_start_time=start_time, + name=sequence.name, + global_start_time=rt_start_time, metadata=metadata ) @@ -353,8 +342,8 @@ def create_otio_track(track_type, track_name): ) -def add_otio_gap(track_item, otio_track, prev_out): - gap_length = track_item.timelineIn() - prev_out +def add_otio_gap(clip_data, otio_track, prev_out): + gap_length = clip_data["record_in"] - prev_out if prev_out != 0: gap_length -= 1 @@ -379,7 +368,7 @@ def add_otio_metadata(otio_item, media_source, **kwargs): for key, value in metadata.items(): otio_item.metadata.update({key: value}) -def get_segment_attributes(segment, frame_rate): +def get_segment_attributes(segment): log.info(segment) if str(segment.name)[1:-1] == "": @@ -391,7 +380,8 @@ def get_segment_attributes(segment, frame_rate): "comment": str(segment.comment)[1:-1], "tape_name": str(segment.tape_name), "source_name": str(segment.source_name), - "fpath": str(segment.file_path) + "fpath": str(segment.file_path), + "segment": segment } # populate shot source metadata @@ -406,119 +396,84 @@ def get_segment_attributes(segment, frame_rate): _value = getattr(segment, attr) segment_attrs_data[attr] = _value _value = str(_value)[1:-1] - clip_data[attr] = utils.timecode_to_frames( - _value, frame_rate) + + if attr in ["record_in", "record_out"]: + # exclude timeline start + frame = utils.timecode_to_frames( + _value, self.fps) + clip_data[attr] = frame - self.seq_frame_start + else: + clip_data[attr] = utils.timecode_to_frames( + _value, self.fps) clip_data["segment_timecodes"] = segment_attrs_data log.info(pformat(clip_data)) return clip_data -def get_track_attributes(track, frame_rate): - log.info(track) - log.info(dir(track)) - log.info(track.attributes) - - if len(track.segments) == 0: - return None - - # Add timeline segment to tree - track_data = { - "name": str(track.name)[1:-1] - } - - # # populate shot source metadata - # segment_attrs = [ - # "record_duration", "record_in", "record_out", - # "source_duration", "source_in", "source_out" - # ] - # segment_attrs_data = {} - # for attr in segment_attrs: - # if not hasattr(segment, attr): - # continue - # _value = getattr(segment, attr) - # segment_attrs_data[attr] = _value - # _value = str(_value)[1:-1] - # clip_data[attr] = utils.timecode_to_frames( - # _value, frame_rate) - - # clip_data["segment_timecodes"] = segment_attrs_data - - log.info(pformat(track_data)) - return track_data - def create_otio_timeline(sequence): log.info(dir(sequence)) log.info(sequence.attributes) # get current timeline self.fps = float(str(sequence.frame_rate)[:-4]) - + self.seq_frame_start = utils.timecode_to_frames( + str(sequence.start_time), self.fps) # # convert timeline to otio otio_timeline = _create_otio_timeline(sequence) # create otio tracks and clips for ver in sequence.versions: for track in ver.tracks: - track_data = get_track_attributes(track, self.fps) - if not track_data: - continue - for segment in track.segments: - # process all segments - clip_data = get_segment_attributes( - segment, self.fps) - # create otio clip - # create otio reference + if len(track.segments) == 0 and track.hidden: + return None + + # convert track to otio + otio_track = create_otio_track( + "video", str(track.name)[1:-1]) + + segments_ordered = { + itemindex: get_segment_attributes(segment) + for itemindex, segment in enumerate( + track.segments) + } + + for itemindex, segment_data in segments_ordered.items(): + # Add Gap if needed + if itemindex == 0: + # if it is first track item at track then add + # it to previouse item + prev_item = segment_data + + else: + # get previouse item + prev_item = segments_ordered[itemindex - 1] + + # calculate clip frame range difference from each other + clip_diff = segment_data["record_in"] - prev_item["record_out"] + + # add gap if first track item is not starting + # at first timeline frame + if itemindex == 0 and segment_data["record_in"] > 0: + add_otio_gap(segment_data, otio_track, 0) + + # or add gap if following track items are having + # frame range differences from each other + elif itemindex and clip_diff != 1: + add_otio_gap( + segment_data, otio_track, prev_item["record_out"]) + + # create otio clip and add it to track + otio_clip = create_otio_clip(segment_data) + otio_track.append(otio_clip) + # create otio marker # create otio metadata + # add track to otio timeline + otio_timeline.tracks.append(otio_track) - # # loop all defined track types - # for track in self.timeline.items(): - # # skip if track is disabled - # if not track.isEnabled(): - # continue - - # # convert track to otio - # otio_track = create_otio_track( - # type(track), track.name()) - - # for itemindex, track_item in enumerate(track): - # # Add Gap if needed - # if itemindex == 0: - # # if it is first track item at track then add - # # it to previouse item - # prev_item = track_item - - # else: - # # get previouse item - # prev_item = track_item.parent().items()[itemindex - 1] - - # # calculate clip frame range difference from each other - # clip_diff = track_item.timelineIn() - prev_item.timelineOut() - - # # add gap if first track item is not starting - # # at first timeline frame - # if itemindex == 0 and track_item.timelineIn() > 0: - # add_otio_gap(track_item, otio_track, 0) - - # # or add gap if following track items are having - # # frame range differences from each other - # elif itemindex and clip_diff != 1: - # add_otio_gap(track_item, otio_track, prev_item.timelineOut()) - - # # create otio clip and add it to track - # otio_clip = create_otio_clip(track_item) - # otio_track.append(otio_clip) - - # # Add tags as markers - # if self.include_tags: - # create_otio_markers(otio_track, track) - - # # add track to otio timeline - # otio_timeline.tracks.append(otio_track) - - # return otio_timeline + return otio_timeline def write_to_file(otio_timeline, path): diff --git a/openpype/hosts/flame/otio/utils.py b/openpype/hosts/flame/otio/utils.py index 2d927957c97..57c8e4fecd0 100644 --- a/openpype/hosts/flame/otio/utils.py +++ b/openpype/hosts/flame/otio/utils.py @@ -28,17 +28,17 @@ def get_reformated_path(path, padded=True): type: string with reformated path Example: - get_reformated_path("plate.[0001-1008].exr") > plate.%04d.exr + get_reformated_path("plate.1001.exr") > plate.%04d.exr """ - if "%" in path: - padding_pattern = r"(\d+)" - padding = int(re.findall(padding_pattern, path).pop()) - num_pattern = r"(%\d+d)" - if padded: - path = re.sub(num_pattern, "%0{}d".format(padding), path) - else: - path = re.sub(num_pattern, "%d", path) + num_pattern = re.compile(r"[._](\d+)[.]") + padding = get_padding_from_path(path) + + if padded: + path = re.sub(num_pattern, "%0{}d".format(padding), path) + else: + path = re.sub(num_pattern, "%d", path) + return path @@ -53,28 +53,14 @@ def get_padding_from_path(path): int: padding number Example: - get_padding_from_path("plate.[0001-1008].exr") > 4 + get_padding_from_path("plate.0001.exr") > 4 """ - padding_pattern = "(\\d+)(?=-)" - if "[" in path: - return len(re.findall(padding_pattern, path).pop()) - - return None - - -def get_rate(item): - if not hasattr(item, 'framerate'): - return None - - num, den = item.framerate().toRational() - - try: - rate = float(num) / float(den) - except ZeroDivisionError: - return None + padding_pattern = re.compile(r"[._](\d+)[.]") - if rate.is_integer(): - return rate + found = re.findall(padding_pattern, path).pop() - return round(rate, 4) + if found: + return len(found) + else: + return None \ No newline at end of file From 8b2edb368bf38c7465a648e868bf331767efb4f4 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 12:23:36 +0100 Subject: [PATCH 232/492] added 2022 variant to aftereffects settings schemas --- .../system_schema/host_settings/schema_aftereffects.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openpype/settings/entities/schemas/system_schema/host_settings/schema_aftereffects.json b/openpype/settings/entities/schemas/system_schema/host_settings/schema_aftereffects.json index 6c36a9bb8a5..334c9aa235c 100644 --- a/openpype/settings/entities/schemas/system_schema/host_settings/schema_aftereffects.json +++ b/openpype/settings/entities/schemas/system_schema/host_settings/schema_aftereffects.json @@ -36,6 +36,11 @@ "app_variant_label": "2021", "app_variant": "2021", "variant_skip_paths": ["use_python_2"] + }, + { + "app_variant_label": "2022", + "app_variant": "2022", + "variant_skip_paths": ["use_python_2"] } ] } From 5989af258a6733ab9dcfdf5da8338a416a9671e8 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 16 Dec 2021 12:38:38 +0100 Subject: [PATCH 233/492] export otio modul wip --- openpype/hosts/flame/otio/flame_export.py | 188 ++++++++++-------- openpype/hosts/flame/otio/utils.py | 44 +++- .../plugins/publish/collect_test_selection.py | 5 +- 3 files changed, 145 insertions(+), 92 deletions(-) diff --git a/openpype/hosts/flame/otio/flame_export.py b/openpype/hosts/flame/otio/flame_export.py index e55e72f55e4..f01c600637a 100644 --- a/openpype/hosts/flame/otio/flame_export.py +++ b/openpype/hosts/flame/otio/flame_export.py @@ -63,7 +63,6 @@ def create_otio_time_range(start_frame, frame_duration, fps): def _get_metadata(item): if hasattr(item, 'metadata'): - log.debug(item.metadata) if not item.metadata: return {} return {key: value for key, value in dict(item.metadata)} @@ -140,6 +139,67 @@ def create_time_effects(otio_clip, track_item): otio_clip.effects.append(otio_effect) +def get_marker_color(tag): + icon = tag.icon() + pat = r'icons:Tag(?P\w+)\.\w+' + + res = re.search(pat, icon) + if res: + color = res.groupdict().get('color') + if color.lower() in self.marker_color_map: + return self.marker_color_map[color.lower()] + + return otio.schema.MarkerColor.RED + + +def create_otio_markers(otio_item, item): + for tag in item.tags(): + if not tag.visible(): + continue + + if tag.name() == 'Copy': + # Hiero adds this tag to a lot of clips + continue + + frame_rate = utils.get_rate(item) or self.fps + + marked_range = otio.opentime.TimeRange( + start_time=otio.opentime.RationalTime( + tag.inTime(), + frame_rate + ), + duration=otio.opentime.RationalTime( + int(tag.metadata().dict().get('tag.length', '0')), + frame_rate + ) + ) + # add tag metadata but remove "tag." string + metadata = {} + + for key, value in tag.metadata().dict().items(): + _key = key.replace("tag.", "") + + try: + # capture exceptions which are related to strings only + _value = ast.literal_eval(value) + except (ValueError, SyntaxError): + _value = value + + metadata.update({_key: _value}) + + # Store the source item for future import assignment + metadata['hiero_source_type'] = item.__class__.__name__ + + marker = otio.schema.Marker( + name=tag.name(), + color=get_marker_color(tag), + marked_range=marked_range, + metadata=metadata + ) + + otio_item.markers.append(marker) + + def create_otio_reference(clip_data): metadata = _get_metadata(clip_data) @@ -150,11 +210,13 @@ def create_otio_reference(clip_data): file_head, extension = os.path.splitext(file_name) # get padding and other file infos - is_sequence = padding = utils.get_padding_from_path(path) + log.debug("_ path: {}".format(path)) + + is_sequence = padding = utils.get_frame_from_path(path) if is_sequence: - padding_pattern = re.compile(r"[._](\d+)[.]") - number = re.findall(padding_pattern, path).pop() + number = utils.get_frame_from_path(path) file_head = file_name.split(number)[:-1] + frame_start = int(number) frame_duration = clip_data["source_duration"] @@ -206,72 +268,14 @@ def create_otio_reference(clip_data): return otio_ex_ref_item -def get_marker_color(tag): - icon = tag.icon() - pat = r'icons:Tag(?P\w+)\.\w+' - - res = re.search(pat, icon) - if res: - color = res.groupdict().get('color') - if color.lower() in self.marker_color_map: - return self.marker_color_map[color.lower()] - - return otio.schema.MarkerColor.RED - - -def create_otio_markers(otio_item, item): - for tag in item.tags(): - if not tag.visible(): - continue - - if tag.name() == 'Copy': - # Hiero adds this tag to a lot of clips - continue - - frame_rate = utils.get_rate(item) or self.fps - - marked_range = otio.opentime.TimeRange( - start_time=otio.opentime.RationalTime( - tag.inTime(), - frame_rate - ), - duration=otio.opentime.RationalTime( - int(tag.metadata().dict().get('tag.length', '0')), - frame_rate - ) - ) - # add tag metadata but remove "tag." string - metadata = {} - - for key, value in tag.metadata().dict().items(): - _key = key.replace("tag.", "") - - try: - # capture exceptions which are related to strings only - _value = ast.literal_eval(value) - except (ValueError, SyntaxError): - _value = value - - metadata.update({_key: _value}) - - # Store the source item for future import assignment - metadata['hiero_source_type'] = item.__class__.__name__ - - marker = otio.schema.Marker( - name=tag.name(), - color=get_marker_color(tag), - marked_range=marked_range, - metadata=metadata - ) - - otio_item.markers.append(marker) - - def create_otio_clip(clip_data): media_reference = create_otio_reference(clip_data) + # calculate source in + first_frame = utils.get_frame_from_path(clip_data["fpath"]) or 0 + source_in = int(clip_data["source_in"]) - int(first_frame) source_range = create_otio_time_range( - clip_data["source_in"], + source_in, clip_data["record_duration"], self.fps ) @@ -282,15 +286,15 @@ def create_otio_clip(clip_data): media_reference=media_reference ) - # Add tags as markers - if self.include_tags: - create_otio_markers(otio_clip, track_item) - create_otio_markers(otio_clip, track_item.source()) + # # Add tags as markers + # if self.include_tags: + # create_otio_markers(otio_clip, track_item) + # create_otio_markers(otio_clip, track_item.source()) - # only if video - if not clip.mediaSource().hasAudio(): - # Add effects to clips - create_time_effects(otio_clip, track_item) + # # only if video + # if not clip.mediaSource().hasAudio(): + # # Add effects to clips + # create_time_effects(otio_clip, track_item) return otio_clip @@ -329,7 +333,7 @@ def _create_otio_timeline(sequence): self.seq_frame_start, self.fps) return otio.schema.Timeline( - name=sequence.name, + name=str(sequence.name)[1:-1], global_start_time=rt_start_time, metadata=metadata ) @@ -369,8 +373,6 @@ def add_otio_metadata(otio_item, media_source, **kwargs): otio_item.metadata.update({key: value}) def get_segment_attributes(segment): - log.info(segment) - if str(segment.name)[1:-1] == "": return None @@ -401,14 +403,13 @@ def get_segment_attributes(segment): # exclude timeline start frame = utils.timecode_to_frames( _value, self.fps) - clip_data[attr] = frame - self.seq_frame_start + clip_data[attr] = (frame - self.seq_frame_start) + 1 else: clip_data[attr] = utils.timecode_to_frames( _value, self.fps) clip_data["segment_timecodes"] = segment_attrs_data - log.info(pformat(clip_data)) return clip_data def create_otio_timeline(sequence): @@ -419,8 +420,10 @@ def create_otio_timeline(sequence): self.fps = float(str(sequence.frame_rate)[:-4]) self.seq_frame_start = utils.timecode_to_frames( str(sequence.start_time), self.fps) - # # convert timeline to otio + + # convert timeline to otio otio_timeline = _create_otio_timeline(sequence) + log.debug("_ otio_timeline: {}".format(otio_timeline)) # create otio tracks and clips for ver in sequence.versions: @@ -432,13 +435,27 @@ def create_otio_timeline(sequence): otio_track = create_otio_track( "video", str(track.name)[1:-1]) + all_segments = [] + for segment in track.segments: + clip_data = get_segment_attributes(segment) + if not clip_data: + continue + all_segments.append(clip_data) + segments_ordered = { - itemindex: get_segment_attributes(segment) - for itemindex, segment in enumerate( - track.segments) + itemindex: clip_data + for itemindex, clip_data in enumerate( + all_segments) } + log.debug("_ segments_ordered: {}".format( + pformat(segments_ordered) + )) + if not segments_ordered: + continue for itemindex, segment_data in segments_ordered.items(): + log.debug("_ itemindex: {}".format(itemindex)) + # Add Gap if needed if itemindex == 0: # if it is first track item at track then add @@ -449,6 +466,9 @@ def create_otio_timeline(sequence): # get previouse item prev_item = segments_ordered[itemindex - 1] + log.debug("_ segment_data: {}".format(segment_data)) + log.debug("_ prev_item: {}".format(prev_item)) + # calculate clip frame range difference from each other clip_diff = segment_data["record_in"] - prev_item["record_out"] @@ -467,6 +487,8 @@ def create_otio_timeline(sequence): otio_clip = create_otio_clip(segment_data) otio_track.append(otio_clip) + log.debug("_ otio_clip: {}".format(otio_clip)) + # create otio marker # create otio metadata diff --git a/openpype/hosts/flame/otio/utils.py b/openpype/hosts/flame/otio/utils.py index 57c8e4fecd0..640a4fabfbd 100644 --- a/openpype/hosts/flame/otio/utils.py +++ b/openpype/hosts/flame/otio/utils.py @@ -1,6 +1,7 @@ import re import opentimelineio as otio - +import logging +log = logging.getLogger(__name__) def timecode_to_frames(timecode, framerate): rt = otio.opentime.from_timecode(timecode, framerate) @@ -31,20 +32,25 @@ def get_reformated_path(path, padded=True): get_reformated_path("plate.1001.exr") > plate.%04d.exr """ - num_pattern = re.compile(r"[._](\d+)[.]") padding = get_padding_from_path(path) + found = get_frame_from_path(path) + + if not found: + log.info("Path is not sequence: {}".format(path)) + return path if padded: - path = re.sub(num_pattern, "%0{}d".format(padding), path) + path = path.replace(found, "%0{}d".format(padding)) else: - path = re.sub(num_pattern, "%d", path) + path = path.replace(found, "%d") + return path def get_padding_from_path(path): """ - Return padding number from DaVinci Resolve sequence path style + Return padding number from Flame path style Args: path (str): path url or simple file name @@ -56,11 +62,33 @@ def get_padding_from_path(path): get_padding_from_path("plate.0001.exr") > 4 """ - padding_pattern = re.compile(r"[._](\d+)[.]") - - found = re.findall(padding_pattern, path).pop() + found = get_frame_from_path(path) if found: return len(found) + else: + return None + +def get_frame_from_path(path): + """ + Return sequence number from Flame path style + + Args: + path (str): path url or simple file name + + Returns: + int: sequence frame number + + Example: + def get_frame_from_path(path): + ("plate.0001.exr") > 0001 + + """ + frame_pattern = re.compile(r"[._](\d+)[.]") + + found = re.findall(frame_pattern, path) + + if found: + return found.pop() else: return None \ No newline at end of file diff --git a/openpype/hosts/flame/plugins/publish/collect_test_selection.py b/openpype/hosts/flame/plugins/publish/collect_test_selection.py index cefd9ee7cfc..4bb99c107b2 100644 --- a/openpype/hosts/flame/plugins/publish/collect_test_selection.py +++ b/openpype/hosts/flame/plugins/publish/collect_test_selection.py @@ -1,7 +1,9 @@ import pyblish.api import openpype.hosts.flame as opflame from openpype.hosts.flame.otio import flame_export as otio_export +from openpype.hosts.flame.api import lib from pprint import pformat +reload(lib) reload(otio_export) @pyblish.api.log @@ -16,7 +18,8 @@ class CollectTestSelection(pyblish.api.ContextPlugin): def process(self, context): self.log.info(opflame.selection) - sequence = opflame.get_current_sequence(opflame.selection) + sequence = lib.get_current_sequence(opflame.selection) + otio_timeline = otio_export.create_otio_timeline(sequence) self.log.info(pformat(otio_timeline)) \ No newline at end of file From 8f622281cf7300ea3bc92912ea133cbcfbef51e2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 13:45:31 +0100 Subject: [PATCH 234/492] change override state on calling pop --- openpype/settings/entities/dict_mutable_keys_entity.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openpype/settings/entities/dict_mutable_keys_entity.py b/openpype/settings/entities/dict_mutable_keys_entity.py index cff346e9ea2..be78018ebbb 100644 --- a/openpype/settings/entities/dict_mutable_keys_entity.py +++ b/openpype/settings/entities/dict_mutable_keys_entity.py @@ -60,6 +60,12 @@ def __contains__(self, key): def pop(self, key, *args, **kwargs): if key in self.required_keys: raise RequiredKeyModified(self.path, key) + + if self._override_state is OverrideState.STUDIO: + self._has_studio_override = True + elif self._override_state is OverrideState.PROJECT: + self._has_project_override = True + result = self.children_by_key.pop(key, *args, **kwargs) self.on_change() return result From 5fc7307be6e74df213fad40941cd86e22f1ccec6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 14:56:00 +0100 Subject: [PATCH 235/492] added ability to pass environment variables --- linux_app_launcher.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/linux_app_launcher.py b/linux_app_launcher.py index 274ef8b82b3..6dc15183706 100644 --- a/linux_app_launcher.py +++ b/linux_app_launcher.py @@ -28,6 +28,12 @@ def main(input_json_path): with open(input_json_path, "r") as stream: data = json.load(stream) + # Change environment variables + env = data.get("env") or {} + for key, value in env.items(): + os.environ[key] = value + + # Prepare launch arguments args = data["args"] if isinstance(args, list): args = subprocess.list2cmdline(args) From dd99682df3284e4a5e2ada02adc4da9414603cb5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 14:57:02 +0100 Subject: [PATCH 236/492] pass environments to set into the json instead of changing them for the subprocess --- openpype/lib/applications.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/lib/applications.py b/openpype/lib/applications.py index 97cc2136466..2101303771c 100644 --- a/openpype/lib/applications.py +++ b/openpype/lib/applications.py @@ -938,8 +938,10 @@ def _run_process(self): # Prepare data that will be passed to midprocess # - store arguments to a json and pass path to json as last argument + # - pass environments to set json_data = { - "args": self.launch_args + "args": self.launch_args, + "env": self.kwargs.pop("env", {}) } # Create temp file json_temp = tempfile.NamedTemporaryFile( From 16ae2913863602b003c548eb7e126c2e741a3ac8 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 15:46:45 +0100 Subject: [PATCH 237/492] adde abstract method to be able know if entity has children by a path key --- openpype/settings/entities/base_entity.py | 5 +++++ .../settings/entities/dict_conditional.py | 3 +++ .../entities/dict_immutable_keys_entity.py | 3 +++ .../entities/dict_mutable_keys_entity.py | 3 +++ openpype/settings/entities/input_entities.py | 3 +++ openpype/settings/entities/item_entities.py | 21 +++++++++++++++++++ openpype/settings/entities/list_entity.py | 16 ++++++++++++++ openpype/settings/entities/root_entities.py | 3 +++ 8 files changed, 57 insertions(+) diff --git a/openpype/settings/entities/base_entity.py b/openpype/settings/entities/base_entity.py index 341968bd75e..cbc042d29d9 100644 --- a/openpype/settings/entities/base_entity.py +++ b/openpype/settings/entities/base_entity.py @@ -235,6 +235,11 @@ def get_entity_from_path(self, path): """Return system settings entity.""" pass + @abstractmethod + def has_child_with_key(self, key): + """Entity contains key as children.""" + pass + def schema_validations(self): """Validate schema of entity and it's hierachy. diff --git a/openpype/settings/entities/dict_conditional.py b/openpype/settings/entities/dict_conditional.py index 5f1c172f311..92512a66689 100644 --- a/openpype/settings/entities/dict_conditional.py +++ b/openpype/settings/entities/dict_conditional.py @@ -107,6 +107,9 @@ def set(self, value): for _key, _value in new_value.items(): self.non_gui_children[self.current_enum][_key].set(_value) + def has_child_with_key(self, key): + return key in self.keys() + def _item_initialization(self): self._default_metadata = NOT_SET self._studio_override_metadata = NOT_SET diff --git a/openpype/settings/entities/dict_immutable_keys_entity.py b/openpype/settings/entities/dict_immutable_keys_entity.py index 6131fa2ac7f..c477a0eb0f8 100644 --- a/openpype/settings/entities/dict_immutable_keys_entity.py +++ b/openpype/settings/entities/dict_immutable_keys_entity.py @@ -205,6 +205,9 @@ def _item_initialization(self): ) self.show_borders = self.schema_data.get("show_borders", True) + def has_child_with_key(self, key): + return key in self.non_gui_children + def collect_static_entities_by_path(self): output = {} if self.is_dynamic_item or self.is_in_dynamic_item: diff --git a/openpype/settings/entities/dict_mutable_keys_entity.py b/openpype/settings/entities/dict_mutable_keys_entity.py index cff346e9ea2..97af9e5c812 100644 --- a/openpype/settings/entities/dict_mutable_keys_entity.py +++ b/openpype/settings/entities/dict_mutable_keys_entity.py @@ -191,6 +191,9 @@ def set_key_label(self, key, label): child_entity = self.children_by_key[key] self.set_child_label(child_entity, label) + def has_child_with_key(self, key): + return key in self.children_by_key + def _item_initialization(self): self._default_metadata = {} self._studio_override_metadata = {} diff --git a/openpype/settings/entities/input_entities.py b/openpype/settings/entities/input_entities.py index a0598d405ed..16893747a6b 100644 --- a/openpype/settings/entities/input_entities.py +++ b/openpype/settings/entities/input_entities.py @@ -118,6 +118,9 @@ def __eq__(self, other): return self.value == other.value return self.value == other + def has_child_with_key(self, key): + return False + def get_child_path(self, child_obj): raise TypeError("{} can't have children".format( self.__class__.__name__ diff --git a/openpype/settings/entities/item_entities.py b/openpype/settings/entities/item_entities.py index ff0a9829007..9c6f428b97d 100644 --- a/openpype/settings/entities/item_entities.py +++ b/openpype/settings/entities/item_entities.py @@ -1,3 +1,7 @@ +import re + +import six + from .lib import ( NOT_SET, STRING_TYPE, @@ -48,6 +52,9 @@ def items(self): raise AttributeError(self.attribute_error_msg.format("items")) return self.child_obj.items() + def has_child_with_key(self, key): + return self.child_obj.has_child_with_key(key) + def _item_initialization(self): if self.group_item is None and not self.is_group: self.is_group = True @@ -197,6 +204,7 @@ def reset_callbacks(self): class ListStrictEntity(ItemEntity): schema_types = ["list-strict"] + _key_regex = re.compile(r"[0-9]+") def __getitem__(self, idx): if not isinstance(idx, int): @@ -216,6 +224,19 @@ def get(self, idx, default=None): return self.children[idx] return default + def has_child_with_key(self, key): + if ( + key + and isinstance(key, six.string_types) + and self._key_regex.match(key) + ): + key = int(key) + + if not isinstance(key, int): + return False + + return 0 <= key < len(self.children) + def _item_initialization(self): self.valid_value_types = (list, ) self.require_key = True diff --git a/openpype/settings/entities/list_entity.py b/openpype/settings/entities/list_entity.py index 5d89a81351e..0268c208bb9 100644 --- a/openpype/settings/entities/list_entity.py +++ b/openpype/settings/entities/list_entity.py @@ -1,4 +1,6 @@ import copy +import six +import re from . import ( BaseEntity, EndpointEntity @@ -21,6 +23,7 @@ class ListEntity(EndpointEntity): "collapsible": True, "collapsed": False } + _key_regex = re.compile(r"[0-9]+") def __iter__(self): for item in self.children: @@ -144,6 +147,19 @@ def swap_indexes(self, index_1, index_2): ) self.on_change() + def has_child_with_key(self, key): + if ( + key + and isinstance(key, six.string_types) + and self._key_regex.match(key) + ): + key = int(key) + + if not isinstance(key, int): + return False + + return 0 <= key < len(self.children) + def _convert_to_valid_type(self, value): if isinstance(value, (set, tuple)): return list(value) diff --git a/openpype/settings/entities/root_entities.py b/openpype/settings/entities/root_entities.py index b8baed8a938..687784a3590 100644 --- a/openpype/settings/entities/root_entities.py +++ b/openpype/settings/entities/root_entities.py @@ -127,6 +127,9 @@ def set(self, value): for _key, _value in new_value.items(): self.non_gui_children[_key].set(_value) + def has_child_with_key(self, key): + return key in self.non_gui_children + def keys(self): return self.non_gui_children.keys() From 06fed885ad5e9a5626baa50dd981856ea98eb183 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 15:47:29 +0100 Subject: [PATCH 238/492] breadcrumbs are not brute focing access to child entities by accessing with __getitem__ but checking if 'has_child_with_key' --- .../settings/settings/breadcrumbs_widget.py | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/openpype/tools/settings/settings/breadcrumbs_widget.py b/openpype/tools/settings/settings/breadcrumbs_widget.py index d25cbdc8cb9..7524bc61f06 100644 --- a/openpype/tools/settings/settings/breadcrumbs_widget.py +++ b/openpype/tools/settings/settings/breadcrumbs_widget.py @@ -71,17 +71,35 @@ def has_children(self, path): return True return False + def get_valid_path(self, path): + if not path: + return "" + + path_items = path.split("/") + new_path_items = [] + entity = self.entity + for item in path_items: + if not entity.has_child_with_key(item): + break + + new_path_items.append(item) + entity = entity[item] + + return "/".join(new_path_items) + def is_valid_path(self, path): if not path: return True path_items = path.split("/") - try: - entity = self.entity - for item in path_items: - entity = entity[item] - except Exception: - return False + + entity = self.entity + for item in path_items: + if not entity.has_child_with_key(item): + return False + + entity = entity[item] + return True @@ -436,6 +454,7 @@ def _on_crumb_clicked(self, path): self.change_path(path) def change_path(self, path): + path = self._model.get_valid_path(path) if self._model and not self._model.is_valid_path(path): self._show_address_field() else: From d392885b35de566faaecab0e8e0a1a3a029eeac2 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 16 Dec 2021 15:58:16 +0100 Subject: [PATCH 239/492] OP-2038 - introduced settings for invalid characters to use in ValidateNaming plugin --- .../plugins/publish/validate_naming.py | 44 ++++++++++++++----- .../defaults/project_settings/photoshop.json | 14 +++--- .../schema_project_photoshop.json | 42 +++++++++++++----- 3 files changed, 75 insertions(+), 25 deletions(-) diff --git a/openpype/hosts/photoshop/plugins/publish/validate_naming.py b/openpype/hosts/photoshop/plugins/publish/validate_naming.py index 0fd6794313c..077f7cf1326 100644 --- a/openpype/hosts/photoshop/plugins/publish/validate_naming.py +++ b/openpype/hosts/photoshop/plugins/publish/validate_naming.py @@ -1,3 +1,5 @@ +import re + import pyblish.api import openpype.api from avalon import photoshop @@ -19,20 +21,32 @@ def process(self, context, plugin): and result["instance"] not in failed): failed.append(result["instance"]) + invalid_chars, replace_char = plugin.get_replace_chars() + # Apply pyblish.logic to get the instances for the plug-in instances = pyblish.api.instances_by_plugin(failed, plugin) stub = photoshop.stub() for instance in instances: self.log.info("validate_naming instance {}".format(instance)) - name = instance.data["name"].replace(" ", "_") - name = name.replace(instance.data["family"], '') - instance[0].Name = name - data = stub.read(instance[0]) - data["subset"] = "image" + name - stub.imprint(instance[0], data) + metadata = stub.read(instance[0]) + self.log.info("metadata instance {}".format(metadata)) + layer_name = None + if metadata.get("uuid"): + layer_data = stub.get_layer(metadata["uuid"]) + self.log.info("layer_data {}".format(layer_data)) + if layer_data: + layer_name = re.sub(invalid_chars, + replace_char, + layer_name) + + stub.rename_layer(instance.data["uuid"], layer_name) + + subset_name = re.sub(invalid_chars, replace_char, + instance.data["name"]) - name = stub.PUBLISH_ICON + name - stub.rename_layer(instance.data["uuid"], name) + instance[0].Name = layer_name or subset_name + metadata["subset"] = subset_name + stub.imprint(instance[0], metadata) return True @@ -49,12 +63,22 @@ class ValidateNaming(pyblish.api.InstancePlugin): families = ["image"] actions = [ValidateNamingRepair] + # configured by Settings + invalid_chars = '' + replace_char = '' + def process(self, instance): help_msg = ' Use Repair action (A) in Pyblish to fix it.' msg = "Name \"{}\" is not allowed.{}".format(instance.data["name"], help_msg) - assert " " not in instance.data["name"], msg + assert not re.search(self.invalid_chars, instance.data["name"]), msg msg = "Subset \"{}\" is not allowed.{}".format(instance.data["subset"], help_msg) - assert " " not in instance.data["subset"], msg + assert not re.search(self.invalid_chars, instance.data["subset"]), msg + + + @classmethod + def get_replace_chars(cls): + """Pass values configured in Settings for Repair.""" + return cls.invalid_chars, cls.replace_char \ No newline at end of file diff --git a/openpype/settings/defaults/project_settings/photoshop.json b/openpype/settings/defaults/project_settings/photoshop.json index 0c24c943ec4..db9bf87268e 100644 --- a/openpype/settings/defaults/project_settings/photoshop.json +++ b/openpype/settings/defaults/project_settings/photoshop.json @@ -7,11 +7,6 @@ } }, "publish": { - "ValidateContainers": { - "enabled": true, - "optional": true, - "active": true - }, "CollectRemoteInstances": { "color_code_mapping": [ { @@ -22,6 +17,15 @@ } ] }, + "ValidateContainers": { + "enabled": true, + "optional": true, + "active": true + }, + "ValidateNaming": { + "invalid_chars": "[ \\\\/+\\*\\?\\(\\)\\[\\]\\{\\}:,]", + "replace_char": "_" + }, "ExtractImage": { "formats": [ "png", diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_photoshop.json b/openpype/settings/entities/schemas/projects_schema/schema_project_photoshop.json index ca388de60c0..51ea5b3fe75 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_photoshop.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_photoshop.json @@ -33,16 +33,6 @@ "key": "publish", "label": "Publish plugins", "children": [ - { - "type": "schema_template", - "name": "template_publish_plugin", - "template_data": [ - { - "key": "ValidateContainers", - "label": "ValidateContainers" - } - ] - }, { "type": "dict", "collapsible": true, @@ -108,6 +98,38 @@ } ] }, + { + "type": "schema_template", + "name": "template_publish_plugin", + "template_data": [ + { + "key": "ValidateContainers", + "label": "ValidateContainers" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "ValidateNaming", + "label": "Validate naming of subsets and layers", + "children": [ + { + "type": "label", + "label": "Subset cannot contain invalid characters or extract to file would fail" + }, + { + "type": "text", + "key": "invalid_chars", + "label": "Regex pattern of invalid characters" + }, + { + "type": "text", + "key": "replace_char", + "label": "Replacement character" + } + ] + }, { "type": "dict", "collapsible": true, From 9ec197acdaf7d064842214bd4540e8dec261ddc0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 16:53:35 +0100 Subject: [PATCH 240/492] added method get_timer_data_for_context to be able get timers related data for passed context --- .../timers_manager/timers_manager.py | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/openpype/modules/default_modules/timers_manager/timers_manager.py b/openpype/modules/default_modules/timers_manager/timers_manager.py index 0f165ff0ac7..6541a9197c6 100644 --- a/openpype/modules/default_modules/timers_manager/timers_manager.py +++ b/openpype/modules/default_modules/timers_manager/timers_manager.py @@ -151,6 +151,44 @@ def tray_exit(self): self._idle_manager.stop() self._idle_manager.wait() + def get_timer_data_for_context(self, project_name, asset_name, task_name): + """Prepare data for timer related callbacks.""" + dbconn = AvalonMongoDB() + dbconn.install() + dbconn.Session["AVALON_PROJECT"] = project_name + + asset_doc = dbconn.find_one( + { + "type": "asset", + "name": asset_name + }, + { + "data.tasks": True, + "data.parents": True + } + ) + if not asset_doc: + raise ValueError("Uknown asset {}".format(asset_name)) + + asset_data = asset_doc.get("data") or {} + task_type = "" + try: + task_type = asset_data["tasks"][task_name]["type"] + except KeyError: + self.log.warning( + "Couldn't find task_type for {}".format(task_name) + ) + + hierarchy_items = asset_data.get("parents") or [] + hierarchy_items.append(asset_name) + + return { + "project_name": project_name, + "task_name": task_name, + "task_type": task_type, + "hierarchy": hierarchy_items + } + def start_timer(self, project_name, asset_name, task_name, hierarchy): """ Start timer for 'project_name', 'asset_name' and 'task_name' From 246bf8f1403cb9bc40cab5498eddb85f771cabe6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 16:54:16 +0100 Subject: [PATCH 241/492] use get_timer_data_for_context in start_timer and don't expect hierarchy --- .../timers_manager/rest_api.py | 15 ++++--- .../timers_manager/timers_manager.py | 45 ++++--------------- 2 files changed, 17 insertions(+), 43 deletions(-) diff --git a/openpype/modules/default_modules/timers_manager/rest_api.py b/openpype/modules/default_modules/timers_manager/rest_api.py index 19b72d688b3..4296610c239 100644 --- a/openpype/modules/default_modules/timers_manager/rest_api.py +++ b/openpype/modules/default_modules/timers_manager/rest_api.py @@ -39,17 +39,18 @@ def register(self): async def start_timer(self, request): data = await request.json() try: - project_name = data['project_name'] - asset_name = data['asset_name'] - task_name = data['task_name'] - hierarchy = data['hierarchy'] + project_name = data["project_name"] + asset_name = data["asset_name"] + task_name = data["task_name"] except KeyError: - log.error("Payload must contain fields 'project_name, " + - "'asset_name', 'task_name', 'hierarchy'") + log.error(( + "Payload must contain fields 'project_name," + " 'asset_name' and 'task_name'" + )) return Response(status=400) self.module.stop_timers() - self.module.start_timer(project_name, asset_name, task_name, hierarchy) + self.module.start_timer(project_name, asset_name, task_name) return Response(status=200) async def stop_timer(self, request): diff --git a/openpype/modules/default_modules/timers_manager/timers_manager.py b/openpype/modules/default_modules/timers_manager/timers_manager.py index 6541a9197c6..fc9897b022b 100644 --- a/openpype/modules/default_modules/timers_manager/timers_manager.py +++ b/openpype/modules/default_modules/timers_manager/timers_manager.py @@ -189,44 +189,17 @@ def get_timer_data_for_context(self, project_name, asset_name, task_name): "hierarchy": hierarchy_items } - def start_timer(self, project_name, asset_name, task_name, hierarchy): - """ - Start timer for 'project_name', 'asset_name' and 'task_name' - - Called from REST api by hosts. + def start_timer(self, project_name, asset_name, task_name): + """Start timer for passed context. - Args: - project_name (string) - asset_name (string) - task_name (string) - hierarchy (string) + Args: + project_name (str): Project name + asset_name (str): Asset name + task_name (str): Task name """ - dbconn = AvalonMongoDB() - dbconn.install() - dbconn.Session["AVALON_PROJECT"] = project_name - - asset_doc = dbconn.find_one({ - "type": "asset", "name": asset_name - }) - if not asset_doc: - raise ValueError("Uknown asset {}".format(asset_name)) - - task_type = '' - try: - task_type = asset_doc["data"]["tasks"][task_name]["type"] - except KeyError: - self.log.warning("Couldn't find task_type for {}". - format(task_name)) - - hierarchy = hierarchy.split("\\") - hierarchy.append(asset_name) - - data = { - "project_name": project_name, - "task_name": task_name, - "task_type": task_type, - "hierarchy": hierarchy - } + data = self.get_timer_data_for_context( + project_name, asset_name, task_name + ) self.timer_started(None, data) def get_task_time(self, project_name, asset_name, task_name): From 83887b918a507d7a7ffce528c39f6c0ac7227415 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 16:55:05 +0100 Subject: [PATCH 242/492] changed 'change_timer_from_host' to static method 'start_timer_with_webserver' --- .../timers_manager/timers_manager.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/openpype/modules/default_modules/timers_manager/timers_manager.py b/openpype/modules/default_modules/timers_manager/timers_manager.py index fc9897b022b..8aa8e189021 100644 --- a/openpype/modules/default_modules/timers_manager/timers_manager.py +++ b/openpype/modules/default_modules/timers_manager/timers_manager.py @@ -306,18 +306,29 @@ def webserver_initialization(self, server_manager): self, server_manager ) - def change_timer_from_host(self, project_name, asset_name, task_name): + @staticmethod + def start_timer_with_webserver( + project_name, asset_name, task_name, logger=None + ): """Prepared method for calling change timers on REST api""" webserver_url = os.environ.get("OPENPYPE_WEBSERVER_URL") if not webserver_url: - self.log.warning("Couldn't find webserver url") + msg = "Couldn't find webserver url" + if logger is not None: + logger.warning(msg) + else: + print(msg) return rest_api_url = "{}/timers_manager/start_timer".format(webserver_url) try: import requests except Exception: - self.log.warning("Couldn't start timer") + msg = "Couldn't start timer ('requests' is not available)" + if logger is not None: + logger.warning(msg) + else: + print(msg) return data = { "project_name": project_name, From fb096d0f68b3dc3171b6f9f0802d51d542b89bb2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 16:56:09 +0100 Subject: [PATCH 243/492] don't pass hierarchy in lib function --- openpype/lib/avalon_context.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/openpype/lib/avalon_context.py b/openpype/lib/avalon_context.py index e3bceff2750..cb5bca133da 100644 --- a/openpype/lib/avalon_context.py +++ b/openpype/lib/avalon_context.py @@ -1433,7 +1433,11 @@ def get_creator_by_name(creator_name, case_sensitive=False): @with_avalon def change_timer_to_current_context(): - """Called after context change to change timers""" + """Called after context change to change timers. + + TODO: + - use TimersManager's static method instead of reimplementing it here + """ webserver_url = os.environ.get("OPENPYPE_WEBSERVER_URL") if not webserver_url: log.warning("Couldn't find webserver url") @@ -1448,8 +1452,7 @@ def change_timer_to_current_context(): data = { "project_name": avalon.io.Session["AVALON_PROJECT"], "asset_name": avalon.io.Session["AVALON_ASSET"], - "task_name": avalon.io.Session["AVALON_TASK"], - "hierarchy": get_hierarchy() + "task_name": avalon.io.Session["AVALON_TASK"] } requests.post(rest_api_url, json=data) From c777bc8afcfebac4009ee2d9ab77e9558c8f3483 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 16:56:31 +0100 Subject: [PATCH 244/492] application launch context nad launch hooks have access to modules manager --- openpype/lib/applications.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/openpype/lib/applications.py b/openpype/lib/applications.py index 6eb44a9694e..184a57ea897 100644 --- a/openpype/lib/applications.py +++ b/openpype/lib/applications.py @@ -640,6 +640,10 @@ def app_group(self): def app_name(self): return getattr(self.application, "full_name", None) + @property + def modules_manager(self): + return getattr(self.launch_context, "modules_manager", None) + def validate(self): """Optional validation of launch hook on initialization. @@ -702,9 +706,13 @@ class ApplicationLaunchContext: """ def __init__(self, application, executable, **data): + from openpype.modules import ModulesManager + # Application object self.application = application + self.modules_manager = ModulesManager() + # Logger logger_name = "{}-{}".format(self.__class__.__name__, self.app_name) self.log = PypeLogger.get_logger(logger_name) @@ -812,10 +820,7 @@ def paths_to_launch_hooks(self): paths.append(path) # Load modules paths - from openpype.modules import ModulesManager - - manager = ModulesManager() - paths.extend(manager.collect_launch_hook_paths()) + paths.extend(self.modules_manager.collect_launch_hook_paths()) return paths From 272f4fcc67f5f6ea9d3dffb4eb97b57848389bd5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 16:56:59 +0100 Subject: [PATCH 245/492] added new post launch hook that will trigger start timer using timers manager --- openpype/hooks/post_start_timer.py | 48 ++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 openpype/hooks/post_start_timer.py diff --git a/openpype/hooks/post_start_timer.py b/openpype/hooks/post_start_timer.py new file mode 100644 index 00000000000..be2cec82b7e --- /dev/null +++ b/openpype/hooks/post_start_timer.py @@ -0,0 +1,48 @@ +import os + +from openpype.api import get_project_settings +from openpype.lib import PostLaunchHook + + +class PostStartTimerHook(PostLaunchHook): + """Start timer with TimersManager module. + + This module requires enabled TimerManager module. + """ + order = None + + def execute(self): + project_name = self.data.get("project_name") + asset_name = self.data.get("asset_name") + task_name = self.data.get("task_name") + + missing_context_keys = set() + if not project_name: + missing_context_keys.add("project_name") + if not asset_name: + missing_context_keys.add("asset_name") + if not task_name: + missing_context_keys.add("task_name") + + if missing_context_keys: + missing_keys_str = ", ".join([ + "\"{}\"".format(key) for key in missing_context_keys + ]) + self.log.debug("Hook {} skipped. Missing data keys: {}".format( + self.__class__.__name__, missing_keys_str + )) + return + + timers_manager = self.modules_manager.modules_by_name.get( + "timers_manager" + ) + if not timers_manager or not timers_manager.enabled: + self.log.info(( + "Skipping starting timer because" + " TimersManager is not available." + )) + return + + timers_manager.start_timer_with_webserver( + project_name, asset_name, task_name, logger=self.log + ) From def385836b88a3626e09fb338121ed5223695572 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 16:57:13 +0100 Subject: [PATCH 246/492] removed start timer in ftrack's post launch hook --- .../launch_hooks/post_ftrack_changes.py | 25 +------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/openpype/modules/default_modules/ftrack/launch_hooks/post_ftrack_changes.py b/openpype/modules/default_modules/ftrack/launch_hooks/post_ftrack_changes.py index df16cde2b87..d5a95fad91f 100644 --- a/openpype/modules/default_modules/ftrack/launch_hooks/post_ftrack_changes.py +++ b/openpype/modules/default_modules/ftrack/launch_hooks/post_ftrack_changes.py @@ -52,7 +52,7 @@ def execute(self): ) if entity: self.ftrack_status_change(session, entity, project_name) - self.start_timer(session, entity, ftrack_api) + except Exception: self.log.warning( "Couldn't finish Ftrack procedure.", exc_info=True @@ -160,26 +160,3 @@ def ftrack_status_change(self, session, entity, project_name): " on Ftrack entity type \"{}\"" ).format(next_status_name, entity.entity_type) self.log.warning(msg) - - def start_timer(self, session, entity, _ftrack_api): - """Start Ftrack timer on task from context.""" - self.log.debug("Triggering timer start.") - - user_entity = session.query("User where username is \"{}\"".format( - os.environ["FTRACK_API_USER"] - )).first() - if not user_entity: - self.log.warning( - "Couldn't find user with username \"{}\" in Ftrack".format( - os.environ["FTRACK_API_USER"] - ) - ) - return - - try: - user_entity.start_timer(entity, force=True) - session.commit() - self.log.debug("Timer start triggered successfully.") - - except Exception: - self.log.warning("Couldn't trigger Ftrack timer.", exc_info=True) From 5528f064130663dda83bce12cc60a906f9c54733 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 17:05:19 +0100 Subject: [PATCH 247/492] added few docstrings --- .../timers_manager/timers_manager.py | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/openpype/modules/default_modules/timers_manager/timers_manager.py b/openpype/modules/default_modules/timers_manager/timers_manager.py index 8aa8e189021..5fffb24a5e8 100644 --- a/openpype/modules/default_modules/timers_manager/timers_manager.py +++ b/openpype/modules/default_modules/timers_manager/timers_manager.py @@ -152,7 +152,11 @@ def tray_exit(self): self._idle_manager.wait() def get_timer_data_for_context(self, project_name, asset_name, task_name): - """Prepare data for timer related callbacks.""" + """Prepare data for timer related callbacks. + + TODO: + - return predefined object that has access to asset document etc. + """ dbconn = AvalonMongoDB() dbconn.install() dbconn.Session["AVALON_PROJECT"] = project_name @@ -203,6 +207,11 @@ def start_timer(self, project_name, asset_name, task_name): self.timer_started(None, data) def get_task_time(self, project_name, asset_name, task_name): + """Get total time for passed context. + + TODO: + - convert context to timer data + """ times = {} for module_id, connector in self._connectors_by_module_id.items(): if hasattr(connector, "get_task_time"): @@ -213,6 +222,10 @@ def get_task_time(self, project_name, asset_name, task_name): return times def timer_started(self, source_id, data): + """Connector triggered that timer has started. + + New timer has started for context in data. + """ for module_id, connector in self._connectors_by_module_id.items(): if module_id == source_id: continue @@ -230,6 +243,14 @@ def timer_started(self, source_id, data): self.is_running = True def timer_stopped(self, source_id): + """Connector triggered that hist timer has stopped. + + Should stop all other timers. + + TODO: + - pass context for which timer has stopped to validate if timers are + same and valid + """ for module_id, connector in self._connectors_by_module_id.items(): if module_id == source_id: continue @@ -248,6 +269,7 @@ def restart_timers(self): self.timer_started(None, self.last_task) def stop_timers(self): + """Stop all timers.""" if self.is_running is False: return From 1984f9d8b484f1f37beb4ae9abf1b62448d36df8 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 17:05:42 +0100 Subject: [PATCH 248/492] added helper method for future use --- .../timers_manager/timers_manager.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/openpype/modules/default_modules/timers_manager/timers_manager.py b/openpype/modules/default_modules/timers_manager/timers_manager.py index 5fffb24a5e8..7c8ad255294 100644 --- a/openpype/modules/default_modules/timers_manager/timers_manager.py +++ b/openpype/modules/default_modules/timers_manager/timers_manager.py @@ -151,6 +151,22 @@ def tray_exit(self): self._idle_manager.stop() self._idle_manager.wait() + def get_timer_data_for_path(self, task_path): + """Convert string path to a timer data. + + It is expected that first item is project name, last item is task name + and parent asset name is before task name. + """ + path_items = task_path.split("/") + if len(path_items) < 3: + raise ValueError("Invalid path") + task_name = path_items.pop(-1) + asset_name = path_items.pop(-1) + project_name = path_items.pop(0) + return self.get_timer_data_for_context( + project_name, asset_name, task_name + ) + def get_timer_data_for_context(self, project_name, asset_name, task_name): """Prepare data for timer related callbacks. From f4aa2d17b25b748ba9523c246e2fe86989af77bd Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 17:12:04 +0100 Subject: [PATCH 249/492] added few lines of docstring --- .../timers_manager/timers_manager.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/openpype/modules/default_modules/timers_manager/timers_manager.py b/openpype/modules/default_modules/timers_manager/timers_manager.py index 7c8ad255294..0ae0af4cdb9 100644 --- a/openpype/modules/default_modules/timers_manager/timers_manager.py +++ b/openpype/modules/default_modules/timers_manager/timers_manager.py @@ -348,7 +348,18 @@ def webserver_initialization(self, server_manager): def start_timer_with_webserver( project_name, asset_name, task_name, logger=None ): - """Prepared method for calling change timers on REST api""" + """Prepared method for calling change timers on REST api. + + Webserver must be active. At the moment is Webserver running only when + OpenPype Tray is used. + + Args: + project_name (str): Project name. + asset_name (str): Asset name. + task_name (str): Task name. + logger (logging.Logger): Logger object. Using 'print' if not + passed. + """ webserver_url = os.environ.get("OPENPYPE_WEBSERVER_URL") if not webserver_url: msg = "Couldn't find webserver url" From 63b7b56547cd395fa5336a7a8abe36ea9888edb2 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 16 Dec 2021 17:12:12 +0100 Subject: [PATCH 250/492] OP-2038 - fix use correct value --- openpype/hosts/photoshop/plugins/publish/validate_naming.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/photoshop/plugins/publish/validate_naming.py b/openpype/hosts/photoshop/plugins/publish/validate_naming.py index 077f7cf1326..7c9ad1e9239 100644 --- a/openpype/hosts/photoshop/plugins/publish/validate_naming.py +++ b/openpype/hosts/photoshop/plugins/publish/validate_naming.py @@ -22,6 +22,7 @@ def process(self, context, plugin): failed.append(result["instance"]) invalid_chars, replace_char = plugin.get_replace_chars() + self.log.info("{} --- {}".format(invalid_chars, replace_char)) # Apply pyblish.logic to get the instances for the plug-in instances = pyblish.api.instances_by_plugin(failed, plugin) @@ -37,7 +38,7 @@ def process(self, context, plugin): if layer_data: layer_name = re.sub(invalid_chars, replace_char, - layer_name) + layer_data.name) stub.rename_layer(instance.data["uuid"], layer_name) From 27dc4552eb2adb3dbdbab72d2b06558cec94fbb2 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 16 Dec 2021 17:14:34 +0100 Subject: [PATCH 251/492] OP-2038 - Hound --- openpype/hosts/photoshop/plugins/publish/validate_naming.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/hosts/photoshop/plugins/publish/validate_naming.py b/openpype/hosts/photoshop/plugins/publish/validate_naming.py index 7c9ad1e9239..1635096f4b2 100644 --- a/openpype/hosts/photoshop/plugins/publish/validate_naming.py +++ b/openpype/hosts/photoshop/plugins/publish/validate_naming.py @@ -78,8 +78,7 @@ def process(self, instance): help_msg) assert not re.search(self.invalid_chars, instance.data["subset"]), msg - @classmethod def get_replace_chars(cls): """Pass values configured in Settings for Repair.""" - return cls.invalid_chars, cls.replace_char \ No newline at end of file + return cls.invalid_chars, cls.replace_char From d4112eb3fafbaf2c817d7771c12e40b36701f2a0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 17:15:27 +0100 Subject: [PATCH 252/492] moved the launch hook into timers manager --- .../launch_hooks}/post_start_timer.py | 3 --- .../timers_manager/timers_manager.py | 14 ++++++++++++-- 2 files changed, 12 insertions(+), 5 deletions(-) rename openpype/{hooks => modules/default_modules/timers_manager/launch_hooks}/post_start_timer.py (96%) diff --git a/openpype/hooks/post_start_timer.py b/openpype/modules/default_modules/timers_manager/launch_hooks/post_start_timer.py similarity index 96% rename from openpype/hooks/post_start_timer.py rename to openpype/modules/default_modules/timers_manager/launch_hooks/post_start_timer.py index be2cec82b7e..d6ae0134033 100644 --- a/openpype/hooks/post_start_timer.py +++ b/openpype/modules/default_modules/timers_manager/launch_hooks/post_start_timer.py @@ -1,6 +1,3 @@ -import os - -from openpype.api import get_project_settings from openpype.lib import PostLaunchHook diff --git a/openpype/modules/default_modules/timers_manager/timers_manager.py b/openpype/modules/default_modules/timers_manager/timers_manager.py index 0ae0af4cdb9..964e6d9a586 100644 --- a/openpype/modules/default_modules/timers_manager/timers_manager.py +++ b/openpype/modules/default_modules/timers_manager/timers_manager.py @@ -1,7 +1,10 @@ import os import platform from openpype.modules import OpenPypeModule -from openpype_interfaces import ITrayService +from openpype_interfaces import ( + ITrayService, + ILaunchHookPaths +) from avalon.api import AvalonMongoDB @@ -64,7 +67,7 @@ def timer_stopped(self): self._timers_manager_module.timer_stopped(self._module.id) -class TimersManager(OpenPypeModule, ITrayService): +class TimersManager(OpenPypeModule, ITrayService, ILaunchHookPaths): """ Handles about Timers. Should be able to start/stop all timers at once. @@ -167,6 +170,13 @@ def get_timer_data_for_path(self, task_path): project_name, asset_name, task_name ) + def get_launch_hook_paths(self): + """Implementation of `ILaunchHookPaths`.""" + return os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "launch_hooks" + ) + def get_timer_data_for_context(self, project_name, asset_name, task_name): """Prepare data for timer related callbacks. From f1b21fedf17aa95aeea56c80f20579a8d517c9c8 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 17:21:36 +0100 Subject: [PATCH 253/492] moved job queue module one hierarchy higher from default_modules --- openpype/modules/{default_modules => }/job_queue/__init__.py | 0 .../{default_modules => }/job_queue/job_server/__init__.py | 0 .../{default_modules => }/job_queue/job_server/job_queue_route.py | 0 .../modules/{default_modules => }/job_queue/job_server/jobs.py | 0 .../modules/{default_modules => }/job_queue/job_server/server.py | 0 .../modules/{default_modules => }/job_queue/job_server/utils.py | 0 .../modules/{default_modules => }/job_queue/job_server/workers.py | 0 .../job_queue/job_server/workers_rpc_route.py | 0 .../{default_modules => }/job_queue/job_workers/__init__.py | 0 .../{default_modules => }/job_queue/job_workers/base_worker.py | 0 openpype/modules/{default_modules => }/job_queue/module.py | 0 11 files changed, 0 insertions(+), 0 deletions(-) rename openpype/modules/{default_modules => }/job_queue/__init__.py (100%) rename openpype/modules/{default_modules => }/job_queue/job_server/__init__.py (100%) rename openpype/modules/{default_modules => }/job_queue/job_server/job_queue_route.py (100%) rename openpype/modules/{default_modules => }/job_queue/job_server/jobs.py (100%) rename openpype/modules/{default_modules => }/job_queue/job_server/server.py (100%) rename openpype/modules/{default_modules => }/job_queue/job_server/utils.py (100%) rename openpype/modules/{default_modules => }/job_queue/job_server/workers.py (100%) rename openpype/modules/{default_modules => }/job_queue/job_server/workers_rpc_route.py (100%) rename openpype/modules/{default_modules => }/job_queue/job_workers/__init__.py (100%) rename openpype/modules/{default_modules => }/job_queue/job_workers/base_worker.py (100%) rename openpype/modules/{default_modules => }/job_queue/module.py (100%) diff --git a/openpype/modules/default_modules/job_queue/__init__.py b/openpype/modules/job_queue/__init__.py similarity index 100% rename from openpype/modules/default_modules/job_queue/__init__.py rename to openpype/modules/job_queue/__init__.py diff --git a/openpype/modules/default_modules/job_queue/job_server/__init__.py b/openpype/modules/job_queue/job_server/__init__.py similarity index 100% rename from openpype/modules/default_modules/job_queue/job_server/__init__.py rename to openpype/modules/job_queue/job_server/__init__.py diff --git a/openpype/modules/default_modules/job_queue/job_server/job_queue_route.py b/openpype/modules/job_queue/job_server/job_queue_route.py similarity index 100% rename from openpype/modules/default_modules/job_queue/job_server/job_queue_route.py rename to openpype/modules/job_queue/job_server/job_queue_route.py diff --git a/openpype/modules/default_modules/job_queue/job_server/jobs.py b/openpype/modules/job_queue/job_server/jobs.py similarity index 100% rename from openpype/modules/default_modules/job_queue/job_server/jobs.py rename to openpype/modules/job_queue/job_server/jobs.py diff --git a/openpype/modules/default_modules/job_queue/job_server/server.py b/openpype/modules/job_queue/job_server/server.py similarity index 100% rename from openpype/modules/default_modules/job_queue/job_server/server.py rename to openpype/modules/job_queue/job_server/server.py diff --git a/openpype/modules/default_modules/job_queue/job_server/utils.py b/openpype/modules/job_queue/job_server/utils.py similarity index 100% rename from openpype/modules/default_modules/job_queue/job_server/utils.py rename to openpype/modules/job_queue/job_server/utils.py diff --git a/openpype/modules/default_modules/job_queue/job_server/workers.py b/openpype/modules/job_queue/job_server/workers.py similarity index 100% rename from openpype/modules/default_modules/job_queue/job_server/workers.py rename to openpype/modules/job_queue/job_server/workers.py diff --git a/openpype/modules/default_modules/job_queue/job_server/workers_rpc_route.py b/openpype/modules/job_queue/job_server/workers_rpc_route.py similarity index 100% rename from openpype/modules/default_modules/job_queue/job_server/workers_rpc_route.py rename to openpype/modules/job_queue/job_server/workers_rpc_route.py diff --git a/openpype/modules/default_modules/job_queue/job_workers/__init__.py b/openpype/modules/job_queue/job_workers/__init__.py similarity index 100% rename from openpype/modules/default_modules/job_queue/job_workers/__init__.py rename to openpype/modules/job_queue/job_workers/__init__.py diff --git a/openpype/modules/default_modules/job_queue/job_workers/base_worker.py b/openpype/modules/job_queue/job_workers/base_worker.py similarity index 100% rename from openpype/modules/default_modules/job_queue/job_workers/base_worker.py rename to openpype/modules/job_queue/job_workers/base_worker.py diff --git a/openpype/modules/default_modules/job_queue/module.py b/openpype/modules/job_queue/module.py similarity index 100% rename from openpype/modules/default_modules/job_queue/module.py rename to openpype/modules/job_queue/module.py From fd7ed342337aa4272e499a38ee2dbde58491b626 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 16 Dec 2021 17:21:49 +0100 Subject: [PATCH 254/492] added 'job_queue' module to default modules list --- openpype/modules/base.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/modules/base.py b/openpype/modules/base.py index a1df3cfd14d..b5c491a1c00 100644 --- a/openpype/modules/base.py +++ b/openpype/modules/base.py @@ -41,6 +41,7 @@ "project_manager_action", "settings_action", "standalonepublish_action", + "job_queue", ) From 449487031c37b8e881d30d8e642b4f4f0ed5a71d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 17 Dec 2021 11:37:54 +0100 Subject: [PATCH 255/492] type label can contain links that can lead to settings or open standard urls --- openpype/tools/settings/settings/base.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/openpype/tools/settings/settings/base.py b/openpype/tools/settings/settings/base.py index b10c9588809..8a420c24475 100644 --- a/openpype/tools/settings/settings/base.py +++ b/openpype/tools/settings/settings/base.py @@ -554,7 +554,9 @@ def create_ui(self): def _create_label_ui(self): label = self.entity["label"] label_widget = QtWidgets.QLabel(label, self) + label_widget.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) label_widget.setObjectName("SettingsLabel") + label_widget.linkActivated.connect(self._on_link_activate) layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 5, 0, 5) @@ -570,6 +572,14 @@ def _create_separator_ui(self): layout.setContentsMargins(5, 5, 5, 5) layout.addWidget(splitter_item) + def _on_link_activate(self, url): + if not url.startswith("settings://"): + QtGui.QDesktopServices.openUrl(url) + return + + path = url.replace("settings://", "") + self.category_widget.go_to_fullpath(path) + def set_entity_value(self): pass From c647649de0ee578217b1afde4141fac2789ef145 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 17 Dec 2021 11:38:18 +0100 Subject: [PATCH 256/492] category widget can trigger change of full path --- openpype/tools/settings/settings/categories.py | 1 + openpype/tools/settings/settings/window.py | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/openpype/tools/settings/settings/categories.py b/openpype/tools/settings/settings/categories.py index 029619849ed..724399c443a 100644 --- a/openpype/tools/settings/settings/categories.py +++ b/openpype/tools/settings/settings/categories.py @@ -82,6 +82,7 @@ class SettingsCategoryWidget(QtWidgets.QWidget): state_changed = QtCore.Signal() saved = QtCore.Signal(QtWidgets.QWidget) restart_required_trigger = QtCore.Signal() + full_path_requested = QtCore.Signal(str, str) def __init__(self, user_role, parent=None): super(SettingsCategoryWidget, self).__init__(parent) diff --git a/openpype/tools/settings/settings/window.py b/openpype/tools/settings/settings/window.py index fd0cd1d7cdf..c376e5e91ef 100644 --- a/openpype/tools/settings/settings/window.py +++ b/openpype/tools/settings/settings/window.py @@ -63,7 +63,9 @@ def __init__(self, user_role, parent=None, reset_on_show=True): tab_widget.restart_required_trigger.connect( self._on_restart_required ) + tab_widget.full_path_requested.connect(self._on_full_path_request) + self._header_tab_widget = header_tab_widget self.tab_widgets = tab_widgets def _on_tab_save(self, source_widget): @@ -90,6 +92,14 @@ def _on_state_change(self): if app: app.processEvents() + def _on_full_path_request(self, category, path): + for tab_widget in self.tab_widgets: + if tab_widget.contain_category_key(category): + idx = self._header_tab_widget.indexOf(tab_widget) + self._header_tab_widget.setCurrentIndex(idx) + tab_widget.set_category_path(category, path) + break + def showEvent(self, event): super(MainWidget, self).showEvent(event) if self._reset_on_show: From a5082df4634f35010ed3194b8fc3147ef36aab5a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 17 Dec 2021 11:41:19 +0100 Subject: [PATCH 257/492] added new methods for category widget that can change full path (lead to different category) --- .../tools/settings/settings/categories.py | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/openpype/tools/settings/settings/categories.py b/openpype/tools/settings/settings/categories.py index 724399c443a..d7650b86631 100644 --- a/openpype/tools/settings/settings/categories.py +++ b/openpype/tools/settings/settings/categories.py @@ -269,6 +269,37 @@ def scroll_to(self, widget): # Scroll to widget self.scroll_widget.ensureWidgetVisible(widget) + def go_to_fullpath(self, full_path): + """Full path of settings entity which can lead to different category. + + Args: + full_path (str): Full path to settings entity. It is expected that + path starts with category name ("system_setting" etc.). + """ + if not full_path: + return + items = full_path.split("/") + category = items[0] + path = "" + if len(items) > 1: + path = "/".join(items[1:]) + self.full_path_requested.emit(category, path) + + def contain_category_key(self, category): + """Parent widget ask if category of full path lead to this widget. + + Args: + category (str): The category name. + + Returns: + bool: Passed category lead to this widget. + """ + return False + + def set_category_path(self, category, path): + """Change path of widget based on category full path.""" + pass + def set_path(self, path): self.breadcrumbs_widget.set_path(path) From 61dff2c51cb0b6253affa27604c81af9cabac7ee Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 17 Dec 2021 11:41:38 +0100 Subject: [PATCH 258/492] override required methods for both current categories --- .../tools/settings/settings/categories.py | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/openpype/tools/settings/settings/categories.py b/openpype/tools/settings/settings/categories.py index d7650b86631..b0460859757 100644 --- a/openpype/tools/settings/settings/categories.py +++ b/openpype/tools/settings/settings/categories.py @@ -587,6 +587,14 @@ def _on_hide_studio_overrides(self, state): class SystemWidget(SettingsCategoryWidget): + def contain_category_key(self, category): + if category == "system_settings": + return True + return False + + def set_category_path(self, category, path): + self.breadcrumbs_widget.change_path(path) + def _create_root_entity(self): self.entity = SystemSettings(set_studio_state=False) self.entity.on_change_callbacks.append(self._on_entity_change) @@ -623,6 +631,21 @@ def _on_modify_defaults(self): class ProjectWidget(SettingsCategoryWidget): + def contain_category_key(self, category): + if category in ("project_settings", "project_anatomy"): + return True + return False + + def set_category_path(self, category, path): + if path: + path_items = path.split("/") + if path_items[0] not in ("project_settings", "project_anatomy"): + path = "/".join([category, path]) + else: + path = category + + self.breadcrumbs_widget.change_path(path) + def initialize_attributes(self): self.project_name = None From b79b5369fb3b297016b89242dc69b059f15139e7 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 17 Dec 2021 12:03:53 +0100 Subject: [PATCH 259/492] Merge current avalon-core main --- repos/avalon-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repos/avalon-core b/repos/avalon-core index 85c656fcf9b..4f10fb1255b 160000 --- a/repos/avalon-core +++ b/repos/avalon-core @@ -1 +1 @@ -Subproject commit 85c656fcf9beb06ab92d3d6ce47f6472cf88df54 +Subproject commit 4f10fb1255beb156f23afa1bb8362dfc53d0c6f8 From 3f4510a9c902b7f8d299ee5f73b2ebac8a9a232d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 17 Dec 2021 13:11:21 +0100 Subject: [PATCH 260/492] renamed 'get_build_version' to 'get_installed_version' in openpype --- openpype/lib/openpype_version.py | 6 +++--- openpype/settings/entities/op_version_entity.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/openpype/lib/openpype_version.py b/openpype/lib/openpype_version.py index 9a92454ecab..e3a4e1fa3e8 100644 --- a/openpype/lib/openpype_version.py +++ b/openpype/lib/openpype_version.py @@ -4,7 +4,7 @@ Is meant to be able check OpenPype versions for studio. The logic is dependent on igniter's inner logic of versions. -Keep in mind that all functions except 'get_build_version' does not return +Keep in mind that all functions except 'get_installed_version' does not return OpenPype version located in build but versions available in remote versions repository or locally available. """ @@ -24,13 +24,13 @@ def op_version_control_available(): return True -def get_build_version(): +def get_installed_version(): """Get OpenPype version inside build. This version is not returned by any other functions here. """ if op_version_control_available(): - return get_OpenPypeVersion().get_build_version() + return get_OpenPypeVersion().get_installed_version() return None diff --git a/openpype/settings/entities/op_version_entity.py b/openpype/settings/entities/op_version_entity.py index 62c5bf4ff90..6f6243cfeef 100644 --- a/openpype/settings/entities/op_version_entity.py +++ b/openpype/settings/entities/op_version_entity.py @@ -1,7 +1,7 @@ from openpype.lib.openpype_version import ( get_remote_versions, get_OpenPypeVersion, - get_build_version + get_installed_version ) from .input_entities import TextEntity from .lib import ( @@ -72,7 +72,7 @@ class ProductionVersionsInputEntity(OpenPypeVersionInput): def _get_openpype_versions(self): versions = get_remote_versions(staging=False, production=True) - versions.append(get_build_version()) + versions.append(get_installed_version()) return sorted(versions) From f8e84de363bd3c24cb8970a1bb2f5eacab7197b2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 17 Dec 2021 14:22:15 +0100 Subject: [PATCH 261/492] implemented tool buttons widgets for unreal --- openpype/hosts/unreal/api/tools_ui.py | 158 ++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 openpype/hosts/unreal/api/tools_ui.py diff --git a/openpype/hosts/unreal/api/tools_ui.py b/openpype/hosts/unreal/api/tools_ui.py new file mode 100644 index 00000000000..bcf8dd2c508 --- /dev/null +++ b/openpype/hosts/unreal/api/tools_ui.py @@ -0,0 +1,158 @@ +import sys +from Qt import QtWidgets, QtCore, QtGui + +from openpype import ( + resources, + style +) +from openpype.tools.utils import host_tools +from openpype.tools.utils.lib import qt_app_context + + +class ToolsBtnsWidget(QtWidgets.QWidget): + """Widget containing buttons which are clickable.""" + tool_required = QtCore.Signal(str) + + def __init__(self, parent=None): + super(ToolsBtnsWidget, self).__init__(parent) + + create_btn = QtWidgets.QPushButton("Create...", self) + load_btn = QtWidgets.QPushButton("Load...", self) + publish_btn = QtWidgets.QPushButton("Publish...", self) + manage_btn = QtWidgets.QPushButton("Manage...", self) + experimental_tools_btn = QtWidgets.QPushButton( + "Experimental tools...", self + ) + + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.addWidget(create_btn, 0) + layout.addWidget(load_btn, 0) + layout.addWidget(publish_btn, 0) + layout.addWidget(manage_btn, 0) + layout.addWidget(experimental_tools_btn, 0) + layout.addStretch(1) + + create_btn.clicked.connect(self._on_create) + load_btn.clicked.connect(self._on_load) + publish_btn.clicked.connect(self._on_publish) + manage_btn.clicked.connect(self._on_manage) + experimental_tools_btn.clicked.connect(self._on_experimental) + + def _on_create(self): + self.tool_required.emit("creator") + + def _on_load(self): + self.tool_required.emit("loader") + + def _on_publish(self): + self.tool_required.emit("publish") + + def _on_manage(self): + self.tool_required.emit("sceneinventory") + + def _on_experimental(self): + self.tool_required.emit("experimental_tools") + + +class ToolsDialog(QtWidgets.QDialog): + """Dialog with tool buttons that will stay opened until user close it.""" + def __init__(self, *args, **kwargs): + super(ToolsDialog, self).__init__(*args, **kwargs) + + self.setWindowTitle("OpenPype tools") + icon = QtGui.QIcon(resources.get_openpype_icon_filepath()) + self.setWindowIcon(icon) + + self.setWindowFlags( + QtCore.Qt.Window + | QtCore.Qt.WindowStaysOnTopHint + ) + self.setFocusPolicy(QtCore.Qt.StrongFocus) + + tools_widget = ToolsBtnsWidget(self) + + layout = QtWidgets.QVBoxLayout(self) + layout.addWidget(tools_widget) + + tools_widget.tool_required.connect(self._on_tool_require) + self._tools_widget = tools_widget + + self._first_show = True + + def sizeHint(self): + result = super(ToolsDialog, self).sizeHint() + result.setWidth(result.width() * 2) + return result + + def showEvent(self, event): + super(ToolsDialog, self).showEvent(event) + if self._first_show: + self.setStyleSheet(style.load_stylesheet()) + self._first_show = False + + def _on_tool_require(self, tool_name): + host_tools.show_tool_by_name(tool_name, parent=self) + + +class ToolsPopup(ToolsDialog): + """Popup with tool buttons that will close when loose focus.""" + def __init__(self, *args, **kwargs): + super(ToolsPopup, self).__init__(*args, **kwargs) + + self.setWindowFlags( + QtCore.Qt.FramelessWindowHint + | QtCore.Qt.Popup + ) + + def showEvent(self, event): + super(ToolsPopup, self).showEvent(event) + app = QtWidgets.QApplication.instance() + app.processEvents() + pos = QtGui.QCursor.pos() + self.move(pos) + + +class WindowCache: + """Cached objects and methods to be used in global scope.""" + _dialog = None + _popup = None + _first_show = True + + @classmethod + def _before_show(cls): + """Create QApplication if does not exists yet."""" + if not cls._first_show: + return + + cls._first_show = False + if not QtWidgets.QApplication.instance(): + QtWidgets.QApplication(sys.argv) + + @classmethod + def show_popup(cls): + cls._before_show() + with qt_app_context(): + if cls._popup is None: + cls._popup = ToolsPopup() + + cls._popup.show() + + @classmethod + def show_dialog(cls): + cls._before_show() + with qt_app_context(): + if cls._dialog is None: + cls._dialog = ToolsDialog() + + cls._dialog.show() + cls._dialog.raise_() + cls._dialog.activateWindow() + + +def show_tools_popup(): + WindowCache.show_popup() + + +def show_tools_dialog(): + WindowCache.show_dialog() From 96c88cec25db6feb01aa7a9894582f5f93156fc6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 17 Dec 2021 14:30:30 +0100 Subject: [PATCH 262/492] fix comment --- openpype/hosts/unreal/api/tools_ui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/unreal/api/tools_ui.py b/openpype/hosts/unreal/api/tools_ui.py index bcf8dd2c508..93361c3574e 100644 --- a/openpype/hosts/unreal/api/tools_ui.py +++ b/openpype/hosts/unreal/api/tools_ui.py @@ -121,7 +121,7 @@ class WindowCache: @classmethod def _before_show(cls): - """Create QApplication if does not exists yet."""" + """Create QApplication if does not exists yet.""" if not cls._first_show: return From 8de80ce7737ab53af516f3b0296f3e27d6e4cd42 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 17 Dec 2021 14:35:42 +0100 Subject: [PATCH 263/492] otio export modul wip --- openpype/hosts/flame/otio/flame_export.py | 157 ++++++++++++++++++---- 1 file changed, 130 insertions(+), 27 deletions(-) diff --git a/openpype/hosts/flame/otio/flame_export.py b/openpype/hosts/flame/otio/flame_export.py index f01c600637a..7cadd390b5a 100644 --- a/openpype/hosts/flame/otio/flame_export.py +++ b/openpype/hosts/flame/otio/flame_export.py @@ -23,7 +23,8 @@ } self.fps = None self.seq_frame_start = None - +self.project = None +self.clips = None self.marker_color_map = { "magenta": otio.schema.MarkerColor.MAGENTA, "red": otio.schema.MarkerColor.RED, @@ -45,7 +46,8 @@ def flatten(_list): def get_current_flame_project(): - return flame.project.current_project + project = flame.project.current_project + return project def create_otio_rational_time(frame, fps): @@ -205,7 +207,19 @@ def create_otio_reference(clip_data): # get file info for path and start frame frame_start = 0 + fps = self.fps + path = clip_data["fpath"] + + reel_clip = None + match_reel_clip = [ + clip for clip in self.clips + if clip["fpath"] == path + ] + if match_reel_clip: + reel_clip = match_reel_clip.pop() + fps = reel_clip["fps"] + file_name = os.path.basename(path) file_head, extension = os.path.splitext(file_name) @@ -240,11 +254,11 @@ def create_otio_reference(clip_data): name_suffix=extension, start_frame=frame_start, frame_zero_padding=padding, - rate=self.fps, + rate=fps, available_range=create_otio_time_range( frame_start, frame_duration, - self.fps + fps ) ) except AttributeError: @@ -258,12 +272,12 @@ def create_otio_reference(clip_data): available_range=create_otio_time_range( frame_start, frame_duration, - self.fps + fps ) ) # add metadata to otio item - # add_otio_metadata(otio_ex_ref_item, media_source, **metadata) + add_otio_metadata(otio_ex_ref_item, clip_data, **metadata) return otio_ex_ref_item @@ -281,7 +295,7 @@ def create_otio_clip(clip_data): ) otio_clip = otio.schema.Clip( - name=clip_data["name"], + name=clip_data["segment_name"], source_range=source_range, media_reference=media_reference ) @@ -308,9 +322,39 @@ def create_otio_gap(gap_start, clip_start, tl_start_frame, fps): ) ) +def get_clips_in_reels(project): + output_clips = [] + project_desktop = project.current_workspace.desktop + + for reel_group in project_desktop.reel_groups: + for reel in reel_group.reels: + for clip in reel.clips: + clip_data = { + "PyClip": clip, + "fps": float(str(clip.frame_rate)[:-4]) + } + + attrs = [ + "name", "width", "height", + "ratio", "sample_rate", "bit_depth" + ] + + for attr in attrs: + val = getattr(clip, attr) + clip_data[attr] = val + + version = clip.versions[-1] + track = version.tracks[-1] + for segment in track.segments: + segment_data = get_segment_attributes(segment) + clip_data.update(segment_data) + + output_clips.append(clip_data) + + return output_clips def _create_otio_timeline(sequence): - project = get_current_flame_project() + metadata = _get_metadata(sequence) metadata.update({ @@ -361,8 +405,8 @@ def add_otio_gap(clip_data, otio_track, prev_out): otio_track.append(otio_gap) -def add_otio_metadata(otio_item, media_source, **kwargs): - metadata = _get_metadata(media_source) +def add_otio_metadata(otio_item, item, **kwargs): + metadata = _get_metadata(item) # add additional metadata from kwargs if kwargs: @@ -372,20 +416,78 @@ def add_otio_metadata(otio_item, media_source, **kwargs): for key, value in metadata.items(): otio_item.metadata.update({key: value}) +def get_markers(item, segment=True): + output_markers = [] + + if segment: + segment_tl_in = item.record_in.relative_frame + + for marker in item.markers: + log.debug(marker) + start_frame = marker.location.get_value().relative_frame + + if segment: + start_frame = (start_frame - segment_tl_in) + 1 + + marker_data = { + "name": marker.name.get_value(), + "duration": marker.duration.get_value().relative_frame, + "comment": marker.comment.get_value(), + "start_frame": start_frame, + "colour": marker.colour.get_value() + } + + output_markers.append(marker_data) + + return output_markers + +def get_shot_tokens_values(clip, tokens): + old_value = None + output = {} + + shot_name = getattr(clip, "shot_name") + + if not shot_name: + return output + + old_value = shot_name.get_value() + + for token in tokens: + shot_name.set_value(token) + _key = re.sub("[ <>]", "", token) + try: + output[_key] = int(shot_name.get_value()) + except: + output[_key] = shot_name.get_value() + + shot_name.set_value(old_value) + + return output + def get_segment_attributes(segment): + # log.debug(dir(segment)) + + markers = get_markers(segment) + if str(segment.name)[1:-1] == "": return None # Add timeline segment to tree clip_data = { - "name": str(segment.name)[1:-1], - "comment": str(segment.comment)[1:-1], - "tape_name": str(segment.tape_name), - "source_name": str(segment.source_name), - "fpath": str(segment.file_path), - "segment": segment + "segment_name": segment.name.get_value(), + "segment_comment": segment.comment.get_value(), + "tape_name": segment.tape_name, + "source_name": segment.source_name, + "fpath": segment.file_path, + "PySegment": segment } + # add all available shot tokens + shot_tokens = get_shot_tokens_values(segment, [ + "", "", "", "", + ]) + clip_data.update(shot_tokens) + # populate shot source metadata segment_attrs = [ "record_duration", "record_in", "record_out", @@ -396,17 +498,12 @@ def get_segment_attributes(segment): if not hasattr(segment, attr): continue _value = getattr(segment, attr) - segment_attrs_data[attr] = _value - _value = str(_value)[1:-1] + segment_attrs_data[attr] = str(_value).replace("+", ":") if attr in ["record_in", "record_out"]: - # exclude timeline start - frame = utils.timecode_to_frames( - _value, self.fps) - clip_data[attr] = (frame - self.seq_frame_start) + 1 + clip_data[attr] = _value.relative_frame else: - clip_data[attr] = utils.timecode_to_frames( - _value, self.fps) + clip_data[attr] = _value.frame clip_data["segment_timecodes"] = segment_attrs_data @@ -416,14 +513,21 @@ def create_otio_timeline(sequence): log.info(dir(sequence)) log.info(sequence.attributes) + self.project = get_current_flame_project() + self.clips = get_clips_in_reels(self.project) + + log.debug(pformat( + self.clips + )) + # get current timeline self.fps = float(str(sequence.frame_rate)[:-4]) self.seq_frame_start = utils.timecode_to_frames( - str(sequence.start_time), self.fps) + str(sequence.start_time).replace("+", ":"), + self.fps) # convert timeline to otio otio_timeline = _create_otio_timeline(sequence) - log.debug("_ otio_timeline: {}".format(otio_timeline)) # create otio tracks and clips for ver in sequence.versions: @@ -467,7 +571,6 @@ def create_otio_timeline(sequence): prev_item = segments_ordered[itemindex - 1] log.debug("_ segment_data: {}".format(segment_data)) - log.debug("_ prev_item: {}".format(prev_item)) # calculate clip frame range difference from each other clip_diff = segment_data["record_in"] - prev_item["record_out"] From de5a4ca0a184115efcbccbef5f5d3b64a4b964c1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 17 Dec 2021 17:48:25 +0100 Subject: [PATCH 264/492] added functin to parse environment data based on passed information --- openpype/lib/applications.py | 94 ++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/openpype/lib/applications.py b/openpype/lib/applications.py index 6eb44a9694e..bbbd335a732 100644 --- a/openpype/lib/applications.py +++ b/openpype/lib/applications.py @@ -41,6 +41,100 @@ _logger = None +PLATFORM_NAMES = {"windows", "linux", "darwin"} +DEFAULT_ENV_SUBGROUP = "standard" + + +def parse_environments(env_data, env_group=None, platform_name=None): + """Parse environment values from settings byt group and platfrom. + + Data may contain up to 2 hierarchical levels of dictionaries. At the end + of the last level must be string or list. List is joined using platform + specific joiner (';' for windows and ':' for linux and mac). + + Hierarchical levels can contain keys for subgroups and platform name. + Platform specific values must be always last level of dictionary. Platform + names are "windows" (MS Windows), "linux" (any linux distribution) and + "darwin" (any MacOS distribution). + + Subgroups are helpers added mainly for standard and on farm usage. Farm + may require different environments for e.g. licence related values or + plugins. Default subgroup is "standard". + + Examples: + ``` + { + # Unchanged value + "ENV_KEY1": "value", + # Empty values are kept (unset environment variable) + "ENV_KEY2": "", + + # Join list values with ':' or ';' + "ENV_KEY3": ["value1", "value2"], + + # Environment groups + "ENV_KEY4": { + "standard": "DEMO_SERVER_URL", + "farm": "LICENCE_SERVER_URL" + }, + + # Platform specific (and only for windows and mac) + "ENV_KEY5": { + "windows": "windows value", + "darwin": ["value 1", "value 2"] + }, + + # Environment groups and platform combination + "ENV_KEY6": { + "farm": "FARM_VALUE", + "standard": { + "windows": ["value1", "value2"], + "linux": "value1", + "darwin": "" + } + } + } + ``` + + Args: + + """ + output = {} + if not env_data: + return output + + if not env_group: + env_group = DEFAULT_ENV_SUBGROUP + + if not platform_name: + platform_name = platform.system().lower() + + for key, value in env_data.items(): + if isinstance(value, dict): + # Look if any key is platform key + # - expect that represents environment group if does not contain + # platform keys + if not PLATFORM_NAMES.intersection(set(value.keys())): + # Skip the key if group is not available + if env_group not in value: + continue + value = value[env_group] + + # Check again if value is dictionary + # - this time there should be only platform keys + if isinstance(value, dict): + value = value.get(platform_name) + + # Check if value is list and join it's values + # QUESTION Should empty values be skipped? + if isinstance(value, (list, tuple)): + value = os.pathsep.join(value) + + # Set key to output if value is string + if isinstance(value, six.string_types): + output[key] = value + return output + def get_logger(): """Global lib.applications logger getter.""" From e3532074c50e9cf1e6f1342fa896f8201dff568a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 17 Dec 2021 17:49:51 +0100 Subject: [PATCH 265/492] environment group is part of all environment related functions and application launch context --- openpype/hooks/pre_global_host_data.py | 2 +- openpype/lib/applications.py | 28 +++++++++++++++++--------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/openpype/hooks/pre_global_host_data.py b/openpype/hooks/pre_global_host_data.py index b32fb5e44a2..6b08cdb444c 100644 --- a/openpype/hooks/pre_global_host_data.py +++ b/openpype/hooks/pre_global_host_data.py @@ -48,7 +48,7 @@ def execute(self): "log": self.log }) - prepare_host_environments(temp_data) + prepare_host_environments(temp_data, self.launch_context.env_group) prepare_context_environments(temp_data) temp_data.pop("log") diff --git a/openpype/lib/applications.py b/openpype/lib/applications.py index bbbd335a732..2e301adf03b 100644 --- a/openpype/lib/applications.py +++ b/openpype/lib/applications.py @@ -795,7 +795,7 @@ class ApplicationLaunchContext: preparation to store objects usable in multiple places. """ - def __init__(self, application, executable, **data): + def __init__(self, application, executable, env_group=None, **data): # Application object self.application = application @@ -805,6 +805,11 @@ def __init__(self, application, executable, **data): self.executable = executable + if env_group is None: + env_group = DEFAULT_ENV_SUBGROUP + + self.env_group = env_group + self.data = dict(data) # subprocess.Popen launch arguments (first argument in constructor) @@ -1141,7 +1146,7 @@ def __init__(self, data): def get_app_environments_for_context( - project_name, asset_name, task_name, app_name, env=None + project_name, asset_name, task_name, app_name, env_group=None, env=None ): """Prepare environment variables by context. Args: @@ -1193,8 +1198,8 @@ def get_app_environments_for_context( "env": env }) - prepare_host_environments(data) - prepare_context_environments(data) + prepare_host_environments(data, env_group) + prepare_context_environments(data, env_group) # Discard avalon connection dbcon.uninstall() @@ -1214,7 +1219,7 @@ def _merge_env(env, current_env): return result -def prepare_host_environments(data, implementation_envs=True): +def prepare_host_environments(data, env_group=None, implementation_envs=True): """Modify launch environments based on launched app and context. Args: @@ -1268,7 +1273,7 @@ def prepare_host_environments(data, implementation_envs=True): continue # Choose right platform - tool_env = acre.parse(_env_values) + tool_env = parse_environments(_env_values, env_group) # Merge dictionaries env_values = _merge_env(tool_env, env_values) @@ -1300,7 +1305,9 @@ def prepare_host_environments(data, implementation_envs=True): data["env"].pop(key, None) -def apply_project_environments_value(project_name, env, project_settings=None): +def apply_project_environments_value( + project_name, env, project_settings=None, env_group=None +): """Apply project specific environments on passed environments. The enviornments are applied on passed `env` argument value so it is not @@ -1328,14 +1335,15 @@ def apply_project_environments_value(project_name, env, project_settings=None): env_value = project_settings["global"]["project_environments"] if env_value: + parsed_value = parse_environments(env_value, env_group) env.update(acre.compute( - _merge_env(acre.parse(env_value), env), + _merge_env(parsed_value, env), cleanup=False )) return env -def prepare_context_environments(data): +def prepare_context_environments(data, env_group=None): """Modify launch environemnts with context data for launched host. Args: @@ -1365,7 +1373,7 @@ def prepare_context_environments(data): data["project_settings"] = project_settings # Apply project specific environments on current env value apply_project_environments_value( - project_name, data["env"], project_settings + project_name, data["env"], project_settings, env_group ) app = data["app"] From 4de80689ed7666cfd87866d591872e2052accf1e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 17 Dec 2021 17:54:02 +0100 Subject: [PATCH 266/492] added environment group to extractenvironments command line argument --- openpype/cli.py | 7 +++++-- openpype/lib/applications.py | 3 --- openpype/pype_commands.py | 9 ++++++--- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/openpype/cli.py b/openpype/cli.py index 6b20fb52037..6e9c237b0e1 100644 --- a/openpype/cli.py +++ b/openpype/cli.py @@ -138,7 +138,10 @@ def webpublisherwebserver(debug, executable, upload_dir, host=None, port=None): @click.option("--asset", help="Asset name", default=None) @click.option("--task", help="Task name", default=None) @click.option("--app", help="Application name", default=None) -def extractenvironments(output_json_path, project, asset, task, app): +@click.option( + "--envgroup", help="Environment group (e.g. \"farm\")", default=None +) +def extractenvironments(output_json_path, project, asset, task, app, envgroup): """Extract environment variables for entered context to a json file. Entered output filepath will be created if does not exists. @@ -149,7 +152,7 @@ def extractenvironments(output_json_path, project, asset, task, app): Context options are "project", "asset", "task", "app" """ PypeCommands.extractenvironments( - output_json_path, project, asset, task, app + output_json_path, project, asset, task, app, envgroup ) diff --git a/openpype/lib/applications.py b/openpype/lib/applications.py index 2e301adf03b..bf52daba7c3 100644 --- a/openpype/lib/applications.py +++ b/openpype/lib/applications.py @@ -95,9 +95,6 @@ def parse_environments(env_data, env_group=None, platform_name=None): } } ``` - - Args: - """ output = {} if not env_data: diff --git a/openpype/pype_commands.py b/openpype/pype_commands.py index a6330bae1fd..e25b56744e8 100644 --- a/openpype/pype_commands.py +++ b/openpype/pype_commands.py @@ -305,13 +305,16 @@ def remotepublish(project, batch_path, user, targets=None): log.info("Publish finished.") @staticmethod - def extractenvironments(output_json_path, project, asset, task, app): - env = os.environ.copy() + def extractenvironments( + output_json_path, project, asset, task, app, env_group + ): if all((project, asset, task, app)): from openpype.api import get_app_environments_for_context env = get_app_environments_for_context( - project, asset, task, app, env + project, asset, task, app, env_group ) + else: + env = os.environ.copy() output_dir = os.path.dirname(output_json_path) if not os.path.exists(output_dir): From eb465b4d266c444193dcfe83f09693ba7e817abf Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 17 Dec 2021 17:55:39 +0100 Subject: [PATCH 267/492] deadline global plugin is using new argument "envgroup" --- vendor/deadline/custom/plugins/GlobalJobPreLoad.py | 1 + 1 file changed, 1 insertion(+) diff --git a/vendor/deadline/custom/plugins/GlobalJobPreLoad.py b/vendor/deadline/custom/plugins/GlobalJobPreLoad.py index 0aa5adaa209..ba1e5f6c6a5 100644 --- a/vendor/deadline/custom/plugins/GlobalJobPreLoad.py +++ b/vendor/deadline/custom/plugins/GlobalJobPreLoad.py @@ -48,6 +48,7 @@ def inject_openpype_environment(deadlinePlugin): add_args['asset'] = job.GetJobEnvironmentKeyValue('AVALON_ASSET') add_args['task'] = job.GetJobEnvironmentKeyValue('AVALON_TASK') add_args['app'] = job.GetJobEnvironmentKeyValue('AVALON_APP_NAME') + add_args["envgroup"] = "farm" if all(add_args.values()): for key, value in add_args.items(): From 4653de69428b4c658d6cbab99c2c676e4f1b1c22 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 17 Dec 2021 18:19:01 +0100 Subject: [PATCH 268/492] wiretap handle for metadata --- openpype/hosts/flame/api/lib.py | 45 +++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index fba2d8f5c84..b7b815b3731 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -290,3 +290,48 @@ def rescan_hooks(): flame.execute_shortcut('Rescan Python Hooks') except Exception: pass + + + +def get_metadata(project_name, _log=None): + import flame + + from adsk.libwiretapPythonClientAPI import ( + WireTapClient, + WireTapServerHandle, + WireTapNodeHandle, + WireTapStr, + WireTapInt + ) + + class GetProjectColorPolicy(object): + def __init__(self, host_name=None, _log=None): + # Create a connection to the Backburner manager using the Wiretap + # python API. + # + self.log = _log or log + self.host_name = host_name or "localhost" + self._wiretap_client = WireTapClient() + if not self._wiretap_client.init(): + raise Exception("Could not initialize Wiretap Client") + self._server = WireTapServerHandle( + "{}:IFFFS".format(self.host_name)) + + def process(self, project_name): + policy_node_handle = WireTapNodeHandle( + self._server, "/projects/{}/syncolor/policy".format(project_name)) + self.log.info(policy_node_handle) + + policy = WireTapStr() + if not policy_node_handle.getNodeTypeStr(policy): + self.log.warning( + "Could not retrieve policy of '%s': %s" % ( + policy_node_handle.getNodeId().id(), + policy_node_handle.lastError() + ) + ) + + return policy.c_str() + + policy_wiretap = GetProjectColorPolicy(_log=_log) + return policy_wiretap.process(project_name) \ No newline at end of file From f73b9b3822dc3769e4f20c4685b39ea8d07044ac Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 17 Dec 2021 18:25:09 +0100 Subject: [PATCH 269/492] getting projects color policy to otio timeline metadata --- openpype/hosts/flame/otio/flame_export.py | 36 +++++++++++++------ .../plugins/publish/collect_test_selection.py | 3 +- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/openpype/hosts/flame/otio/flame_export.py b/openpype/hosts/flame/otio/flame_export.py index 7cadd390b5a..e6ceafb3a68 100644 --- a/openpype/hosts/flame/otio/flame_export.py +++ b/openpype/hosts/flame/otio/flame_export.py @@ -353,24 +353,38 @@ def get_clips_in_reels(project): return output_clips +def _get_colourspace_policy(): + + output = {} + # get policies project path + policy_dir = "/opt/Autodesk/project/{}/synColor/policy".format( + self.project.name + ) + log.debug(policy_dir) + policy_fp = os.path.join(policy_dir, "policy.cfg") + + if not os.path.exists(policy_fp): + return output + + with open(policy_fp) as file: + dict_conf = dict(line.strip().split(' = ', 1) for line in file) + output.update( + {"openpype.flame.{}".format(k): v for k, v in dict_conf.items()} + ) + return output + def _create_otio_timeline(sequence): metadata = _get_metadata(sequence) + # find colour policy files and add them to metadata + colorspace_policy = _get_colourspace_policy() + metadata.update(colorspace_policy) + metadata.update({ "openpype.timeline.width": int(sequence.width), "openpype.timeline.height": int(sequence.height), - "openpype.timeline.pixelAspect": 1, # noqa - # "openpype.project.useOCIOEnvironmentOverride": project.useOCIOEnvironmentOverride(), # noqa - # "openpype.project.lutSetting16Bit": project.lutSetting16Bit(), - # "openpype.project.lutSetting8Bit": project.lutSetting8Bit(), - # "openpype.project.lutSettingFloat": project.lutSettingFloat(), - # "openpype.project.lutSettingLog": project.lutSettingLog(), - # "openpype.project.lutSettingViewer": project.lutSettingViewer(), - # "openpype.project.lutSettingWorkingSpace": project.lutSettingWorkingSpace(), # noqa - # "openpype.project.lutUseOCIOForExport": project.lutUseOCIOForExport(), - # "openpype.project.ocioConfigName": project.ocioConfigName(), - # "openpype.project.ocioConfigPath": project.ocioConfigPath() + "openpype.timeline.pixelAspect": 1 }) rt_start_time = create_otio_rational_time( diff --git a/openpype/hosts/flame/plugins/publish/collect_test_selection.py b/openpype/hosts/flame/plugins/publish/collect_test_selection.py index 4bb99c107b2..41847ad9b6e 100644 --- a/openpype/hosts/flame/plugins/publish/collect_test_selection.py +++ b/openpype/hosts/flame/plugins/publish/collect_test_selection.py @@ -6,6 +6,7 @@ reload(lib) reload(otio_export) + @pyblish.api.log class CollectTestSelection(pyblish.api.ContextPlugin): """testing selection sharing @@ -22,4 +23,4 @@ def process(self, context): otio_timeline = otio_export.create_otio_timeline(sequence) - self.log.info(pformat(otio_timeline)) \ No newline at end of file + self.log.info(pformat(otio_timeline)) From db4dc04d8f5f8052c9e98c2c4212748ad52b4aed Mon Sep 17 00:00:00 2001 From: OpenPype Date: Sat, 18 Dec 2021 03:42:07 +0000 Subject: [PATCH 270/492] [Automated] Bump version --- CHANGELOG.md | 26 ++++++++------------------ openpype/version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 10 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dde81386295..1eb8455a095 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## [3.7.0-nightly.8](https://github.com/pypeclub/OpenPype/tree/HEAD) +## [3.7.0-nightly.9](https://github.com/pypeclub/OpenPype/tree/HEAD) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.6.4...HEAD) @@ -14,6 +14,8 @@ **🚀 Enhancements** +- Settings UI: Hyperlinks to settings [\#2420](https://github.com/pypeclub/OpenPype/pull/2420) +- Modules: JobQueue module moved one hierarchy level higher [\#2419](https://github.com/pypeclub/OpenPype/pull/2419) - Ftrack: Check existence of object type on recreation [\#2404](https://github.com/pypeclub/OpenPype/pull/2404) - Flame: moving `utility\_scripts` to api folder also with `scripts` [\#2385](https://github.com/pypeclub/OpenPype/pull/2385) - Centos 7 dependency compatibility [\#2384](https://github.com/pypeclub/OpenPype/pull/2384) @@ -29,13 +31,14 @@ - General: OpenPype default modules hierarchy [\#2338](https://github.com/pypeclub/OpenPype/pull/2338) - General: FFprobe error exception contain original error message [\#2328](https://github.com/pypeclub/OpenPype/pull/2328) - Resolve: Add experimental button to menu [\#2325](https://github.com/pypeclub/OpenPype/pull/2325) -- Input links: Cleanup and unification of differences [\#2322](https://github.com/pypeclub/OpenPype/pull/2322) -- General: Don't validate vendor bin with executing them [\#2317](https://github.com/pypeclub/OpenPype/pull/2317) -- General: Multilayer EXRs support [\#2315](https://github.com/pypeclub/OpenPype/pull/2315) - General: Reduce vendor imports [\#2305](https://github.com/pypeclub/OpenPype/pull/2305) +- Ftrack: Synchronize input links [\#2287](https://github.com/pypeclub/OpenPype/pull/2287) **🐛 Bug fixes** +- PS: Introduced settings for invalid characters to use in ValidateNaming plugin [\#2417](https://github.com/pypeclub/OpenPype/pull/2417) +- Settings UI: Breadcrumbs path does not create new entities [\#2416](https://github.com/pypeclub/OpenPype/pull/2416) +- AfterEffects: Variant 2022 is in defaults but missing in schemas [\#2412](https://github.com/pypeclub/OpenPype/pull/2412) - General: Fix access to environments from default settings [\#2403](https://github.com/pypeclub/OpenPype/pull/2403) - Fix: Placeholder Input color set fix [\#2399](https://github.com/pypeclub/OpenPype/pull/2399) - Settings: Fix state change of wrapper label [\#2396](https://github.com/pypeclub/OpenPype/pull/2396) @@ -55,8 +58,7 @@ - Tools: Use Qt context on tools show [\#2340](https://github.com/pypeclub/OpenPype/pull/2340) - Flame: Fix default argument value in custom dictionary [\#2339](https://github.com/pypeclub/OpenPype/pull/2339) - Timers Manager: Disable auto stop timer on linux platform [\#2334](https://github.com/pypeclub/OpenPype/pull/2334) -- nuke: bake preset single input exception [\#2331](https://github.com/pypeclub/OpenPype/pull/2331) -- Hiero: fixing multiple templates at a hierarchy parent [\#2330](https://github.com/pypeclub/OpenPype/pull/2330) +- Fix - provider icons are pulled from a folder [\#2326](https://github.com/pypeclub/OpenPype/pull/2326) - Royal Render: Fix plugin order and OpenPype auto-detection [\#2291](https://github.com/pypeclub/OpenPype/pull/2291) **Merged pull requests:** @@ -66,7 +68,6 @@ - Linux : flip updating submodules logic [\#2357](https://github.com/pypeclub/OpenPype/pull/2357) - Update of avalon-core [\#2346](https://github.com/pypeclub/OpenPype/pull/2346) - Maya: configurable model top level validation [\#2321](https://github.com/pypeclub/OpenPype/pull/2321) -- Create test publish class for After Effects [\#2270](https://github.com/pypeclub/OpenPype/pull/2270) ## [3.6.4](https://github.com/pypeclub/OpenPype/tree/3.6.4) (2021-11-23) @@ -88,17 +89,6 @@ [Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.6.2-nightly.2...3.6.2) -**🚀 Enhancements** - -- Tools: Assets widget [\#2265](https://github.com/pypeclub/OpenPype/pull/2265) -- SceneInventory: Choose loader in asset switcher [\#2262](https://github.com/pypeclub/OpenPype/pull/2262) - -**🐛 Bug fixes** - -- Tools: Parenting of tools in Nuke and Hiero [\#2266](https://github.com/pypeclub/OpenPype/pull/2266) -- limiting validator to specific editorial hosts [\#2264](https://github.com/pypeclub/OpenPype/pull/2264) -- Tools: Select Context dialog attribute fix [\#2261](https://github.com/pypeclub/OpenPype/pull/2261) - ## [3.6.1](https://github.com/pypeclub/OpenPype/tree/3.6.1) (2021-11-16) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.6.1-nightly.1...3.6.1) diff --git a/openpype/version.py b/openpype/version.py index 06bc20ae433..544160d41c9 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.7.0-nightly.8" +__version__ = "3.7.0-nightly.9" diff --git a/pyproject.toml b/pyproject.toml index e5d552bb3b6..07a9ac8e433 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.7.0-nightly.8" # OpenPype +version = "3.7.0-nightly.9" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From 65fa8aa90476db7d1d803574bafd712c7d2a10d6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 20 Dec 2021 10:36:56 +0100 Subject: [PATCH 271/492] added validation of installed third party libraries before build --- setup.py | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index cd3ed4f82cb..92cc76dc7a4 100644 --- a/setup.py +++ b/setup.py @@ -3,15 +3,65 @@ import os import sys import re +import platform +import distutils.spawn from pathlib import Path from cx_Freeze import setup, Executable from sphinx.setup_command import BuildDoc -version = {} - openpype_root = Path(os.path.dirname(__file__)) + +def validate_thirdparty_binaries(): + """Check existence of thirdpart executables.""" + low_platform = platform.system().lower() + binary_vendors_dir = os.path.join( + openpype_root, + "vendor", + "bin" + ) + + error_msg = ( + "Missing binary dependency {}. Please fetch thirdparty dependencies." + ) + # Validate existence of FFmpeg + ffmpeg_dir = os.path.join(binary_vendors_dir, "ffmpeg", low_platform) + if low_platform == "windows": + ffmpeg_dir = os.path.join(ffmpeg_dir, "bin") + ffmpeg_executable = os.path.join(ffmpeg_dir, "ffmpeg") + ffmpeg_result = distutils.spawn.find_executable(ffmpeg_executable) + if ffmpeg_result is None: + raise RuntimeError(error_msg.format("FFmpeg")) + + # Validate existence of OpenImageIO (not on MacOs) + oiio_tool_path = None + if low_platform == "linux": + oiio_tool_path = os.path.join( + binary_vendors_dir, + "oiio", + low_platform, + "bin", + "oiiotool" + ) + elif low_platform == "windows": + oiio_tool_path = os.path.join( + binary_vendors_dir, + "oiio", + low_platform, + "oiiotool" + ) + oiio_result = None + if oiio_tool_path is not None: + oiio_result = distutils.spawn.find_executable(oiio_tool_path) + if oiio_result is None: + raise RuntimeError(error_msg.format("OpenImageIO")) + + +validate_thirdparty_binaries() + +version = {} + with open(openpype_root / "openpype" / "version.py") as fp: exec(fp.read(), version) From 5a194263fde0b9821d29479a26d92c0d5c3fe510 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 20 Dec 2021 13:10:17 +0100 Subject: [PATCH 272/492] get_timer_data_for_context is static method --- .../timers_manager/timers_manager.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/openpype/modules/default_modules/timers_manager/timers_manager.py b/openpype/modules/default_modules/timers_manager/timers_manager.py index 964e6d9a586..11e18219128 100644 --- a/openpype/modules/default_modules/timers_manager/timers_manager.py +++ b/openpype/modules/default_modules/timers_manager/timers_manager.py @@ -167,7 +167,7 @@ def get_timer_data_for_path(self, task_path): asset_name = path_items.pop(-1) project_name = path_items.pop(0) return self.get_timer_data_for_context( - project_name, asset_name, task_name + project_name, asset_name, task_name, self.log ) def get_launch_hook_paths(self): @@ -177,7 +177,10 @@ def get_launch_hook_paths(self): "launch_hooks" ) - def get_timer_data_for_context(self, project_name, asset_name, task_name): + @staticmethod + def get_timer_data_for_context( + project_name, asset_name, task_name, logger=None + ): """Prepare data for timer related callbacks. TODO: @@ -205,9 +208,11 @@ def get_timer_data_for_context(self, project_name, asset_name, task_name): try: task_type = asset_data["tasks"][task_name]["type"] except KeyError: - self.log.warning( - "Couldn't find task_type for {}".format(task_name) - ) + msg = "Couldn't find task_type for {}".format(task_name) + if logger is not None: + logger.warning(msg) + else: + print(msg) hierarchy_items = asset_data.get("parents") or [] hierarchy_items.append(asset_name) @@ -228,7 +233,7 @@ def start_timer(self, project_name, asset_name, task_name): task_name (str): Task name """ data = self.get_timer_data_for_context( - project_name, asset_name, task_name + project_name, asset_name, task_name, self.log ) self.timer_started(None, data) From 03ba413b282e1ce8b4c2d63a2371617d16c0592a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 20 Dec 2021 13:13:10 +0100 Subject: [PATCH 273/492] adde new exception --- openpype/modules/default_modules/timers_manager/exceptions.py | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 openpype/modules/default_modules/timers_manager/exceptions.py diff --git a/openpype/modules/default_modules/timers_manager/exceptions.py b/openpype/modules/default_modules/timers_manager/exceptions.py new file mode 100644 index 00000000000..5a9e00765da --- /dev/null +++ b/openpype/modules/default_modules/timers_manager/exceptions.py @@ -0,0 +1,3 @@ +class InvalidContextError(ValueError): + """Context for which the timer should be started is invalid.""" + pass From 3bb3211d4a63d6a93666e6c854f3f2d068fbe5e2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 20 Dec 2021 13:13:42 +0100 Subject: [PATCH 274/492] raise InvalidContextError if context is not valid --- .../timers_manager/timers_manager.py | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/openpype/modules/default_modules/timers_manager/timers_manager.py b/openpype/modules/default_modules/timers_manager/timers_manager.py index 11e18219128..051b2d7c80b 100644 --- a/openpype/modules/default_modules/timers_manager/timers_manager.py +++ b/openpype/modules/default_modules/timers_manager/timers_manager.py @@ -1,11 +1,14 @@ import os import platform + +from avalon.api import AvalonMongoDB + from openpype.modules import OpenPypeModule from openpype_interfaces import ( ITrayService, ILaunchHookPaths ) -from avalon.api import AvalonMongoDB +from .exceptions import InvalidContextError class ExampleTimersManagerConnector: @@ -162,7 +165,7 @@ def get_timer_data_for_path(self, task_path): """ path_items = task_path.split("/") if len(path_items) < 3: - raise ValueError("Invalid path") + raise InvalidContextError("Invalid path \"{}\"".format(task_path)) task_name = path_items.pop(-1) asset_name = path_items.pop(-1) project_name = path_items.pop(0) @@ -186,6 +189,12 @@ def get_timer_data_for_context( TODO: - return predefined object that has access to asset document etc. """ + if not project_name or not asset_name or not task_name: + raise InvalidContextError(( + "Missing context information got" + " Project: \"{}\" Asset: \"{}\" Task: \"{}\"" + ).format(str(project_name), str(asset_name), str(task_name))) + dbconn = AvalonMongoDB() dbconn.install() dbconn.Session["AVALON_PROJECT"] = project_name @@ -201,12 +210,22 @@ def get_timer_data_for_context( } ) if not asset_doc: - raise ValueError("Uknown asset {}".format(asset_name)) + dbconn.uninstall() + raise InvalidContextError(( + "Asset \"{}\" not found in project \"{}\"" + ).format(asset_name, project_name)) asset_data = asset_doc.get("data") or {} + asset_tasks = asset_data.get("tasks") or {} + if task_name not in asset_tasks: + dbconn.uninstall() + raise InvalidContextError(( + "Task \"{}\" not found on asset \"{}\" in project \"{}\"" + ).format(task_name, asset_name, project_name)) + task_type = "" try: - task_type = asset_data["tasks"][task_name]["type"] + task_type = asset_tasks[task_name]["type"] except KeyError: msg = "Couldn't find task_type for {}".format(task_name) if logger is not None: @@ -217,6 +236,7 @@ def get_timer_data_for_context( hierarchy_items = asset_data.get("parents") or [] hierarchy_items.append(asset_name) + dbconn.uninstall() return { "project_name": project_name, "task_name": task_name, From ddcdff611e55ed6dadfccf598ca882a70c56cdf2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 20 Dec 2021 13:14:18 +0100 Subject: [PATCH 275/492] return error message in response --- .../default_modules/timers_manager/rest_api.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/openpype/modules/default_modules/timers_manager/rest_api.py b/openpype/modules/default_modules/timers_manager/rest_api.py index 4296610c239..f16cb316c3d 100644 --- a/openpype/modules/default_modules/timers_manager/rest_api.py +++ b/openpype/modules/default_modules/timers_manager/rest_api.py @@ -43,14 +43,19 @@ async def start_timer(self, request): asset_name = data["asset_name"] task_name = data["task_name"] except KeyError: - log.error(( + msg = ( "Payload must contain fields 'project_name," " 'asset_name' and 'task_name'" - )) - return Response(status=400) + ) + log.error(msg) + return Response(status=400, message=msg) self.module.stop_timers() - self.module.start_timer(project_name, asset_name, task_name) + try: + self.module.start_timer(project_name, asset_name, task_name) + except Exception as exc: + return Response(status=404, message=str(exc)) + return Response(status=200) async def stop_timer(self, request): From 9605abfb13f64356aa382ef3e4ec0182345a1cae Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 20 Dec 2021 13:14:54 +0100 Subject: [PATCH 276/492] return response from posted request --- .../modules/default_modules/timers_manager/timers_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/default_modules/timers_manager/timers_manager.py b/openpype/modules/default_modules/timers_manager/timers_manager.py index 051b2d7c80b..47d020104b2 100644 --- a/openpype/modules/default_modules/timers_manager/timers_manager.py +++ b/openpype/modules/default_modules/timers_manager/timers_manager.py @@ -420,4 +420,4 @@ def start_timer_with_webserver( "task_name": task_name } - requests.post(rest_api_url, json=data) + return requests.post(rest_api_url, json=data) From e7760a6508c21074420ca08d39204afd57ec62b3 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 20 Dec 2021 15:08:29 +0100 Subject: [PATCH 277/492] flame otio export module update --- openpype/hosts/flame/otio/flame_export.py | 304 +++++++++++----------- 1 file changed, 153 insertions(+), 151 deletions(-) diff --git a/openpype/hosts/flame/otio/flame_export.py b/openpype/hosts/flame/otio/flame_export.py index e6ceafb3a68..7a2e5744e8f 100644 --- a/openpype/hosts/flame/otio/flame_export.py +++ b/openpype/hosts/flame/otio/flame_export.py @@ -4,7 +4,7 @@ import os import re import sys -import ast +import json import logging import opentimelineio as otio from . import utils @@ -12,7 +12,7 @@ import flame from pprint import pformat -reload(utils) +reload(utils) # noqa log = logging.getLogger(__name__) @@ -31,9 +31,14 @@ "yellow": otio.schema.MarkerColor.YELLOW, "green": otio.schema.MarkerColor.GREEN, "cyan": otio.schema.MarkerColor.CYAN, + "white": otio.schema.MarkerColor.WHITE, + "orange": otio.schema.MarkerColor.ORANGE, "blue": otio.schema.MarkerColor.BLUE, + "purple": otio.schema.MarkerColor.PURPLE, + "pink": otio.schema.MarkerColor.PINK, + "black": otio.schema.MarkerColor.BLACK, } -self.include_tags = True +self.include_markers = True def flatten(_list): @@ -63,6 +68,7 @@ def create_otio_time_range(start_frame, frame_duration, fps): duration=create_otio_rational_time(frame_duration, fps) ) + def _get_metadata(item): if hasattr(item, 'metadata'): if not item.metadata: @@ -71,130 +77,150 @@ def _get_metadata(item): return {} -def create_time_effects(otio_clip, track_item): +def create_time_effects(otio_clip, item): + # todo #2426: add retiming effects to export + pass # get all subtrack items - subTrackItems = flatten(track_item.parent().subTrackItems()) - speed = track_item.playbackSpeed() - - otio_effect = None - # retime on track item - if speed != 1.: - # make effect - otio_effect = otio.schema.LinearTimeWarp() - otio_effect.name = "Speed" - otio_effect.time_scalar = speed - otio_effect.metadata = {} - - # freeze frame effect - if speed == 0.: - otio_effect = otio.schema.FreezeFrame() - otio_effect.name = "FreezeFrame" - otio_effect.metadata = {} - - if otio_effect: - # add otio effect to clip effects - otio_clip.effects.append(otio_effect) - - # loop trought and get all Timewarps - for effect in subTrackItems: - if ((track_item not in effect.linkedItems()) - and (len(effect.linkedItems()) > 0)): - continue - # avoid all effect which are not TimeWarp and disabled - if "TimeWarp" not in effect.name(): - continue - - if not effect.isEnabled(): - continue + # subTrackItems = flatten(track_item.parent().subTrackItems()) + # speed = track_item.playbackSpeed() + + # otio_effect = None + # # retime on track item + # if speed != 1.: + # # make effect + # otio_effect = otio.schema.LinearTimeWarp() + # otio_effect.name = "Speed" + # otio_effect.time_scalar = speed + # otio_effect.metadata = {} + + # # freeze frame effect + # if speed == 0.: + # otio_effect = otio.schema.FreezeFrame() + # otio_effect.name = "FreezeFrame" + # otio_effect.metadata = {} + + # if otio_effect: + # # add otio effect to clip effects + # otio_clip.effects.append(otio_effect) + + # # loop trought and get all Timewarps + # for effect in subTrackItems: + # if ((track_item not in effect.linkedItems()) + # and (len(effect.linkedItems()) > 0)): + # continue + # # avoid all effect which are not TimeWarp and disabled + # if "TimeWarp" not in effect.name(): + # continue + + # if not effect.isEnabled(): + # continue + + # node = effect.node() + # name = node["name"].value() + + # # solve effect class as effect name + # _name = effect.name() + # if "_" in _name: + # effect_name = re.sub(r"(?:_)[_0-9]+", "", _name) # more numbers + # else: + # effect_name = re.sub(r"\d+", "", _name) # one number + + # metadata = {} + # # add knob to metadata + # for knob in ["lookup", "length"]: + # value = node[knob].value() + # animated = node[knob].isAnimated() + # if animated: + # value = [ + # ((node[knob].getValueAt(i)) - i) + # for i in range( + # track_item.timelineIn(), track_item.timelineOut() + 1) + # ] + + # metadata[knob] = value + + # # make effect + # otio_effect = otio.schema.TimeEffect() + # otio_effect.name = name + # otio_effect.effect_name = effect_name + # otio_effect.metadata = metadata + + # # add otio effect to clip effects + # otio_clip.effects.append(otio_effect) + + +def _get_marker_color(flame_colour): + if flame_colour in self.marker_color_map: + return self.marker_color_map[flame_colour] - node = effect.node() - name = node["name"].value() + return otio.schema.MarkerColor.RED - # solve effect class as effect name - _name = effect.name() - if "_" in _name: - effect_name = re.sub(r"(?:_)[_0-9]+", "", _name) # more numbers - else: - effect_name = re.sub(r"\d+", "", _name) # one number - metadata = {} - # add knob to metadata - for knob in ["lookup", "length"]: - value = node[knob].value() - animated = node[knob].isAnimated() - if animated: - value = [ - ((node[knob].getValueAt(i)) - i) - for i in range( - track_item.timelineIn(), track_item.timelineOut() + 1) - ] - - metadata[knob] = value +def _get_flame_markers(item): + output_markers = [] - # make effect - otio_effect = otio.schema.TimeEffect() - otio_effect.name = name - otio_effect.effect_name = effect_name - otio_effect.metadata = metadata + time_in = item.record_in.relative_frame - # add otio effect to clip effects - otio_clip.effects.append(otio_effect) + for marker in item.markers: + log.debug(marker) + start_frame = marker.location.get_value().relative_frame + start_frame = (start_frame - time_in) + 1 -def get_marker_color(tag): - icon = tag.icon() - pat = r'icons:Tag(?P\w+)\.\w+' + marker_data = { + "name": marker.name.get_value(), + "duration": marker.duration.get_value().relative_frame, + "comment": marker.comment.get_value(), + "start_frame": start_frame, + "colour": marker.colour.get_value() + } - res = re.search(pat, icon) - if res: - color = res.groupdict().get('color') - if color.lower() in self.marker_color_map: - return self.marker_color_map[color.lower()] + output_markers.append(marker_data) - return otio.schema.MarkerColor.RED + return output_markers def create_otio_markers(otio_item, item): - for tag in item.tags(): - if not tag.visible(): - continue - - if tag.name() == 'Copy': - # Hiero adds this tag to a lot of clips - continue - - frame_rate = utils.get_rate(item) or self.fps + markers = _get_flame_markers(item) + for marker in markers: + frame_rate = self.fps marked_range = otio.opentime.TimeRange( start_time=otio.opentime.RationalTime( - tag.inTime(), + marker["start_frame"], frame_rate ), duration=otio.opentime.RationalTime( - int(tag.metadata().dict().get('tag.length', '0')), + marker["duration"], frame_rate ) ) - # add tag metadata but remove "tag." string - metadata = {} - for key, value in tag.metadata().dict().items(): - _key = key.replace("tag.", "") + # testing the comment if it is not containing json string + check_if_json = re.findall( + re.compile(r"[{:}]"), + marker["comment"] + ) + # to identify this as json, at least 3 items in the list should + # be present ["{", ":", "}"] + metadata = {} + if len(check_if_json) >= 3: + # this is json string try: # capture exceptions which are related to strings only - _value = ast.literal_eval(value) - except (ValueError, SyntaxError): - _value = value - - metadata.update({_key: _value}) - - # Store the source item for future import assignment - metadata['hiero_source_type'] = item.__class__.__name__ + metadata.update( + json.loads(marker["comment"]) + ) + except ValueError as msg: + log.error("Marker json conversion: {}".format(msg)) + else: + metadata["comment"] = marker["comment"] marker = otio.schema.Marker( - name=tag.name(), - color=get_marker_color(tag), + name=marker["name"], + color=_get_marker_color( + marker["colour"]), marked_range=marked_range, metadata=metadata ) @@ -240,7 +266,6 @@ def create_otio_reference(clip_data): "padding": padding }) - otio_ex_ref_item = None if is_sequence: @@ -283,11 +308,16 @@ def create_otio_reference(clip_data): def create_otio_clip(clip_data): + segment = clip_data["PySegment"] + # create media reference media_reference = create_otio_reference(clip_data) + # calculate source in first_frame = utils.get_frame_from_path(clip_data["fpath"]) or 0 source_in = int(clip_data["source_in"]) - int(first_frame) + + # creatae source range source_range = create_otio_time_range( source_in, clip_data["record_duration"], @@ -300,15 +330,9 @@ def create_otio_clip(clip_data): media_reference=media_reference ) - # # Add tags as markers - # if self.include_tags: - # create_otio_markers(otio_clip, track_item) - # create_otio_markers(otio_clip, track_item.source()) - - # # only if video - # if not clip.mediaSource().hasAudio(): - # # Add effects to clips - # create_time_effects(otio_clip, track_item) + # Add markers + if self.include_markers: + create_otio_markers(otio_clip, segment) return otio_clip @@ -322,6 +346,7 @@ def create_otio_gap(gap_start, clip_start, tl_start_frame, fps): ) ) + def get_clips_in_reels(project): output_clips = [] project_desktop = project.current_workspace.desktop @@ -346,13 +371,14 @@ def get_clips_in_reels(project): version = clip.versions[-1] track = version.tracks[-1] for segment in track.segments: - segment_data = get_segment_attributes(segment) + segment_data = _get_segment_attributes(segment) clip_data.update(segment_data) output_clips.append(clip_data) return output_clips + def _get_colourspace_policy(): output = {} @@ -373,6 +399,7 @@ def _get_colourspace_policy(): ) return output + def _create_otio_timeline(sequence): metadata = _get_metadata(sequence) @@ -430,58 +457,32 @@ def add_otio_metadata(otio_item, item, **kwargs): for key, value in metadata.items(): otio_item.metadata.update({key: value}) -def get_markers(item, segment=True): - output_markers = [] - - if segment: - segment_tl_in = item.record_in.relative_frame - - for marker in item.markers: - log.debug(marker) - start_frame = marker.location.get_value().relative_frame - if segment: - start_frame = (start_frame - segment_tl_in) + 1 - - marker_data = { - "name": marker.name.get_value(), - "duration": marker.duration.get_value().relative_frame, - "comment": marker.comment.get_value(), - "start_frame": start_frame, - "colour": marker.colour.get_value() - } - - output_markers.append(marker_data) - - return output_markers - -def get_shot_tokens_values(clip, tokens): +def _get_shot_tokens_values(clip, tokens): old_value = None output = {} - shot_name = getattr(clip, "shot_name") - - if not shot_name: + if not clip.shot_name: return output - old_value = shot_name.get_value() + old_value = clip.shot_name.get_value() for token in tokens: - shot_name.set_value(token) + clip.shot_name.set_value(token) _key = re.sub("[ <>]", "", token) + try: - output[_key] = int(shot_name.get_value()) - except: - output[_key] = shot_name.get_value() + output[_key] = int(clip.shot_name.get_value()) + except TypeError: + output[_key] = clip.shot_name.get_value() - shot_name.set_value(old_value) + clip.shot_name.set_value(old_value) return output -def get_segment_attributes(segment): - # log.debug(dir(segment)) - markers = get_markers(segment) +def _get_segment_attributes(segment): + # log.debug(dir(segment)) if str(segment.name)[1:-1] == "": return None @@ -497,7 +498,7 @@ def get_segment_attributes(segment): } # add all available shot tokens - shot_tokens = get_shot_tokens_values(segment, [ + shot_tokens = _get_shot_tokens_values(segment, [ "", "", "", "", ]) clip_data.update(shot_tokens) @@ -523,6 +524,7 @@ def get_segment_attributes(segment): return clip_data + def create_otio_timeline(sequence): log.info(dir(sequence)) log.info(sequence.attributes) @@ -537,8 +539,8 @@ def create_otio_timeline(sequence): # get current timeline self.fps = float(str(sequence.frame_rate)[:-4]) self.seq_frame_start = utils.timecode_to_frames( - str(sequence.start_time).replace("+", ":"), - self.fps) + str(sequence.start_time).replace("+", ":"), + self.fps) # convert timeline to otio otio_timeline = _create_otio_timeline(sequence) @@ -555,7 +557,7 @@ def create_otio_timeline(sequence): all_segments = [] for segment in track.segments: - clip_data = get_segment_attributes(segment) + clip_data = _get_segment_attributes(segment) if not clip_data: continue all_segments.append(clip_data) From 54b5dd8d61c18e88391e07d30b0cfa41d7a56214 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 20 Dec 2021 15:35:08 +0100 Subject: [PATCH 278/492] pep8 comply --- openpype/hosts/flame/api/lib.py | 11 +++++------ openpype/hosts/flame/api/menu.py | 2 ++ openpype/hosts/flame/otio/flame_export.py | 3 ++- openpype/hosts/flame/otio/utils.py | 5 +++-- .../flame/plugins/publish/collect_test_selection.py | 4 ++-- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index b7b815b3731..80185579d61 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -258,6 +258,7 @@ def get_current_project(): def get_current_sequence(selection): import flame + def segment_to_sequence(_segment): track = _segment.parent version = track.parent @@ -291,11 +292,7 @@ def rescan_hooks(): except Exception: pass - - def get_metadata(project_name, _log=None): - import flame - from adsk.libwiretapPythonClientAPI import ( WireTapClient, WireTapServerHandle, @@ -319,7 +316,9 @@ def __init__(self, host_name=None, _log=None): def process(self, project_name): policy_node_handle = WireTapNodeHandle( - self._server, "/projects/{}/syncolor/policy".format(project_name)) + self._server, + "/projects/{}/syncolor/policy".format(project_name) + ) self.log.info(policy_node_handle) policy = WireTapStr() @@ -334,4 +333,4 @@ def process(self, project_name): return policy.c_str() policy_wiretap = GetProjectColorPolicy(_log=_log) - return policy_wiretap.process(project_name) \ No newline at end of file + return policy_wiretap.process(project_name) diff --git a/openpype/hosts/flame/api/menu.py b/openpype/hosts/flame/api/menu.py index f216428e18b..893c7a21d05 100644 --- a/openpype/hosts/flame/api/menu.py +++ b/openpype/hosts/flame/api/menu.py @@ -24,12 +24,14 @@ } } + def send_selection(selection): import openpype.hosts.flame as opflame opflame.selection = selection print(opflame.selection) return True + class _FlameMenuApp(object): def __init__(self, framework): self.name = self.__class__.__name__ diff --git a/openpype/hosts/flame/otio/flame_export.py b/openpype/hosts/flame/otio/flame_export.py index 7a2e5744e8f..a9be495bb5e 100644 --- a/openpype/hosts/flame/otio/flame_export.py +++ b/openpype/hosts/flame/otio/flame_export.py @@ -134,7 +134,8 @@ def create_time_effects(otio_clip, item): # value = [ # ((node[knob].getValueAt(i)) - i) # for i in range( - # track_item.timelineIn(), track_item.timelineOut() + 1) + # track_item.timelineIn(), + # track_item.timelineOut() + 1) # ] # metadata[knob] = value diff --git a/openpype/hosts/flame/otio/utils.py b/openpype/hosts/flame/otio/utils.py index 640a4fabfbd..74963bdd738 100644 --- a/openpype/hosts/flame/otio/utils.py +++ b/openpype/hosts/flame/otio/utils.py @@ -3,6 +3,7 @@ import logging log = logging.getLogger(__name__) + def timecode_to_frames(timecode, framerate): rt = otio.opentime.from_timecode(timecode, framerate) return int(otio.opentime.to_frames(rt)) @@ -44,7 +45,6 @@ def get_reformated_path(path, padded=True): else: path = path.replace(found, "%d") - return path @@ -69,6 +69,7 @@ def get_padding_from_path(path): else: return None + def get_frame_from_path(path): """ Return sequence number from Flame path style @@ -91,4 +92,4 @@ def get_frame_from_path(path): if found: return found.pop() else: - return None \ No newline at end of file + return None diff --git a/openpype/hosts/flame/plugins/publish/collect_test_selection.py b/openpype/hosts/flame/plugins/publish/collect_test_selection.py index 41847ad9b6e..9a80a924140 100644 --- a/openpype/hosts/flame/plugins/publish/collect_test_selection.py +++ b/openpype/hosts/flame/plugins/publish/collect_test_selection.py @@ -3,8 +3,8 @@ from openpype.hosts.flame.otio import flame_export as otio_export from openpype.hosts.flame.api import lib from pprint import pformat -reload(lib) -reload(otio_export) +reload(lib) # noqa +reload(otio_export) # noqa @pyblish.api.log From ab4bc22616aa6a552086b5ef597e1a398bbf24b1 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 20 Dec 2021 15:38:15 +0100 Subject: [PATCH 279/492] pep8 comply --- openpype/hosts/flame/api/lib.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index 80185579d61..96bffab7744 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -292,13 +292,13 @@ def rescan_hooks(): except Exception: pass + def get_metadata(project_name, _log=None): from adsk.libwiretapPythonClientAPI import ( WireTapClient, WireTapServerHandle, WireTapNodeHandle, - WireTapStr, - WireTapInt + WireTapStr ) class GetProjectColorPolicy(object): From 5fed85f647c430161ba99f93684a8039ee4ee5a2 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 20 Dec 2021 16:04:49 +0100 Subject: [PATCH 280/492] typo --- openpype/hosts/flame/otio/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/flame/otio/utils.py b/openpype/hosts/flame/otio/utils.py index 74963bdd738..229946343b3 100644 --- a/openpype/hosts/flame/otio/utils.py +++ b/openpype/hosts/flame/otio/utils.py @@ -14,7 +14,7 @@ def frames_to_timecode(frames, framerate): return otio.opentime.to_timecode(rt) -def frames_to_secons(frames, framerate): +def frames_to_seconds(frames, framerate): rt = otio.opentime.from_frames(frames, framerate) return otio.opentime.to_seconds(rt) From 9802d01b0b2798e95e3ec947128edb85682e37af Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 20 Dec 2021 17:30:44 +0100 Subject: [PATCH 281/492] removing self from module and replacing with context class --- openpype/hosts/flame/otio/flame_export.py | 80 ++++++++++++++++------- 1 file changed, 55 insertions(+), 25 deletions(-) diff --git a/openpype/hosts/flame/otio/flame_export.py b/openpype/hosts/flame/otio/flame_export.py index a9be495bb5e..c149d12d706 100644 --- a/openpype/hosts/flame/otio/flame_export.py +++ b/openpype/hosts/flame/otio/flame_export.py @@ -16,16 +16,12 @@ log = logging.getLogger(__name__) -self = sys.modules[__name__] -self.track_types = { + +TRACK_TYPES = { "video": otio.schema.TrackKind.Video, "audio": otio.schema.TrackKind.Audio } -self.fps = None -self.seq_frame_start = None -self.project = None -self.clips = None -self.marker_color_map = { +MARKERS_COLOR_MAP = { "magenta": otio.schema.MarkerColor.MAGENTA, "red": otio.schema.MarkerColor.RED, "yellow": otio.schema.MarkerColor.YELLOW, @@ -38,7 +34,37 @@ "pink": otio.schema.MarkerColor.PINK, "black": otio.schema.MarkerColor.BLACK, } -self.include_markers = True +MARKERS_INCLUDE = True + + +class CTX: + _fps = None + _tl_start_frame = None + project = None + clips = None + + @classmethod + def set_fps(cls, new_fps): + if not isinstance(new_fps, float): + raise TypeError("Invalid fps type {}".format(type(new_fps))) + if cls._fps != new_fps: + cls._fps = new_fps + + @classmethod + def get_fps(cls): + return cls._fps + + @classmethod + def set_tl_start_frame(cls, number): + if not isinstance(number, int): + raise TypeError("Invalid timeline start frame type {}".format( + type(number))) + if cls._tl_start_frame != number: + cls._tl_start_frame = number + + @classmethod + def get_tl_start_frame(cls): + return cls._tl_start_frame def flatten(_list): @@ -151,8 +177,8 @@ def create_time_effects(otio_clip, item): def _get_marker_color(flame_colour): - if flame_colour in self.marker_color_map: - return self.marker_color_map[flame_colour] + if flame_colour in MARKERS_COLOR_MAP: + return MARKERS_COLOR_MAP[flame_colour] return otio.schema.MarkerColor.RED @@ -184,7 +210,7 @@ def _get_flame_markers(item): def create_otio_markers(otio_item, item): markers = _get_flame_markers(item) for marker in markers: - frame_rate = self.fps + frame_rate = CTX.get_fps() marked_range = otio.opentime.TimeRange( start_time=otio.opentime.RationalTime( @@ -234,13 +260,13 @@ def create_otio_reference(clip_data): # get file info for path and start frame frame_start = 0 - fps = self.fps + fps = CTX.get_fps() path = clip_data["fpath"] reel_clip = None match_reel_clip = [ - clip for clip in self.clips + clip for clip in CTX.clips if clip["fpath"] == path ] if match_reel_clip: @@ -322,7 +348,7 @@ def create_otio_clip(clip_data): source_range = create_otio_time_range( source_in, clip_data["record_duration"], - self.fps + CTX.get_fps() ) otio_clip = otio.schema.Clip( @@ -332,7 +358,7 @@ def create_otio_clip(clip_data): ) # Add markers - if self.include_markers: + if MARKERS_INCLUDE: create_otio_markers(otio_clip, segment) return otio_clip @@ -385,7 +411,7 @@ def _get_colourspace_policy(): output = {} # get policies project path policy_dir = "/opt/Autodesk/project/{}/synColor/policy".format( - self.project.name + CTX.project.name ) log.debug(policy_dir) policy_fp = os.path.join(policy_dir, "policy.cfg") @@ -416,7 +442,7 @@ def _create_otio_timeline(sequence): }) rt_start_time = create_otio_rational_time( - self.seq_frame_start, self.fps) + CTX.get_tl_start_frame(), CTX.get_fps()) return otio.schema.Timeline( name=str(sequence.name)[1:-1], @@ -428,7 +454,7 @@ def _create_otio_timeline(sequence): def create_otio_track(track_type, track_name): return otio.schema.Track( name=track_name, - kind=self.track_types[track_type] + kind=TRACK_TYPES[track_type] ) @@ -440,7 +466,7 @@ def add_otio_gap(clip_data, otio_track, prev_out): gap = otio.opentime.TimeRange( duration=otio.opentime.RationalTime( gap_length, - self.fps + CTX.get_fps() ) ) otio_gap = otio.schema.Gap(source_range=gap) @@ -530,18 +556,22 @@ def create_otio_timeline(sequence): log.info(dir(sequence)) log.info(sequence.attributes) - self.project = get_current_flame_project() - self.clips = get_clips_in_reels(self.project) + CTX.project = get_current_flame_project() + CTX.clips = get_clips_in_reels(CTX.project) log.debug(pformat( - self.clips + CTX.clips )) # get current timeline - self.fps = float(str(sequence.frame_rate)[:-4]) - self.seq_frame_start = utils.timecode_to_frames( + CTX.set_fps( + float(str(sequence.frame_rate)[:-4])) + + tl_start_frame = utils.timecode_to_frames( str(sequence.start_time).replace("+", ":"), - self.fps) + CTX.get_fps() + ) + CTX.set_tl_start_frame(tl_start_frame) # convert timeline to otio otio_timeline = _create_otio_timeline(sequence) From 25c6ce08e4965e60da35b7359d73fce4bf011477 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 20 Dec 2021 18:06:42 +0100 Subject: [PATCH 282/492] fix markers creation and token conversion --- openpype/hosts/flame/otio/flame_export.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/flame/otio/flame_export.py b/openpype/hosts/flame/otio/flame_export.py index c149d12d706..6897c4d8186 100644 --- a/openpype/hosts/flame/otio/flame_export.py +++ b/openpype/hosts/flame/otio/flame_export.py @@ -244,7 +244,7 @@ def create_otio_markers(otio_item, item): else: metadata["comment"] = marker["comment"] - marker = otio.schema.Marker( + otio_marker = otio.schema.Marker( name=marker["name"], color=_get_marker_color( marker["colour"]), @@ -252,7 +252,7 @@ def create_otio_markers(otio_item, item): metadata=metadata ) - otio_item.markers.append(marker) + otio_item.markers.append(otio_marker) def create_otio_reference(clip_data): @@ -500,7 +500,7 @@ def _get_shot_tokens_values(clip, tokens): try: output[_key] = int(clip.shot_name.get_value()) - except TypeError: + except ValueError: output[_key] = clip.shot_name.get_value() clip.shot_name.set_value(old_value) From 897cc0946fbd5719f77d98c46eac7c2eb77622f6 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 21 Dec 2021 10:41:52 +0100 Subject: [PATCH 283/492] color schemas --- openpype/hosts/flame/otio/flame_export.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/openpype/hosts/flame/otio/flame_export.py b/openpype/hosts/flame/otio/flame_export.py index 6897c4d8186..356cf1b49e8 100644 --- a/openpype/hosts/flame/otio/flame_export.py +++ b/openpype/hosts/flame/otio/flame_export.py @@ -22,17 +22,17 @@ "audio": otio.schema.TrackKind.Audio } MARKERS_COLOR_MAP = { - "magenta": otio.schema.MarkerColor.MAGENTA, - "red": otio.schema.MarkerColor.RED, - "yellow": otio.schema.MarkerColor.YELLOW, - "green": otio.schema.MarkerColor.GREEN, - "cyan": otio.schema.MarkerColor.CYAN, - "white": otio.schema.MarkerColor.WHITE, - "orange": otio.schema.MarkerColor.ORANGE, - "blue": otio.schema.MarkerColor.BLUE, - "purple": otio.schema.MarkerColor.PURPLE, - "pink": otio.schema.MarkerColor.PINK, - "black": otio.schema.MarkerColor.BLACK, + (1.0, 0.0, 0.0): otio.schema.MarkerColor.RED, + (1.0, 0.5, 0.0): otio.schema.MarkerColor.ORANGE, + (1.0, 1.0, 0.0): otio.schema.MarkerColor.YELLOW, + (1.0, 0.5, 1.0): otio.schema.MarkerColor.PINK, + (1.0, 1.0, 1.0): otio.schema.MarkerColor.WHITE, + (0.0, 1.0, 0.0): otio.schema.MarkerColor.GREEN, + (0.0, 1.0, 1.0): otio.schema.MarkerColor.CYAN, + (0.0, 0.0, 1.0): otio.schema.MarkerColor.BLUE, + (0.5, 0.0, 0.5): otio.schema.MarkerColor.PURPLE, + (0.5, 0.0, 1.0): otio.schema.MarkerColor.MAGENTA, + (0.0, 0.0, 0.0): otio.schema.MarkerColor.BLACK } MARKERS_INCLUDE = True From c814e04691546032b1a6fe2937b2a4a8382396e3 Mon Sep 17 00:00:00 2001 From: 2-REC Date: Tue, 21 Dec 2021 17:35:29 +0700 Subject: [PATCH 284/492] Check min length of plugin path --- openpype/lib/plugin_tools.py | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/openpype/lib/plugin_tools.py b/openpype/lib/plugin_tools.py index 2a859da7cb4..8de5f641eb0 100644 --- a/openpype/lib/plugin_tools.py +++ b/openpype/lib/plugin_tools.py @@ -227,20 +227,27 @@ def filter_pyblish_plugins(plugins): # iterate over plugins for plugin in plugins[:]: - file = os.path.normpath(inspect.getsourcefile(plugin)) - file = os.path.normpath(file) - - # host determined from path - host_from_file = file.split(os.path.sep)[-4:-3][0] - plugin_kind = file.split(os.path.sep)[-2:-1][0] - - # TODO: change after all plugins are moved one level up - if host_from_file == "openpype": - host_from_file = "global" - try: config_data = presets[host]["publish"][plugin.__name__] except KeyError: + # host determined from path + file = os.path.normpath(inspect.getsourcefile(plugin)) + file = os.path.normpath(file) + + split_path = file.split(os.path.sep) + if len(split_path) < 4: + log.warning( + 'plugin path too short to extract host {}'.format(file) + ) + continue + + host_from_file = split_path[-4:-3][0] + plugin_kind = split_path[-2:-1][0] + + # TODO: change after all plugins are moved one level up + if host_from_file == "openpype": + host_from_file = "global" + try: config_data = presets[host_from_file][plugin_kind][plugin.__name__] # noqa: E501 except KeyError: From 6d15256840d39d07b8e4c3d1e4a20f8b715027c5 Mon Sep 17 00:00:00 2001 From: 2-REC Date: Tue, 21 Dec 2021 17:38:39 +0700 Subject: [PATCH 285/492] Simplified indexing --- openpype/lib/plugin_tools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/lib/plugin_tools.py b/openpype/lib/plugin_tools.py index 8de5f641eb0..7c66f9760de 100644 --- a/openpype/lib/plugin_tools.py +++ b/openpype/lib/plugin_tools.py @@ -241,8 +241,8 @@ def filter_pyblish_plugins(plugins): ) continue - host_from_file = split_path[-4:-3][0] - plugin_kind = split_path[-2:-1][0] + host_from_file = split_path[-4] + plugin_kind = split_path[-2] # TODO: change after all plugins are moved one level up if host_from_file == "openpype": From 9b0e2c3e8beccc86fdbfa317080008cfa8e2d9b3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 21 Dec 2021 13:21:31 +0100 Subject: [PATCH 286/492] added new exception PublishXmlValidationError --- openpype/pipeline/publish/__init__.py | 8 ++- openpype/pipeline/publish/lib.py | 57 ++++++++++++++++++++ openpype/pipeline/publish/publish_plugins.py | 25 ++++++++- 3 files changed, 87 insertions(+), 3 deletions(-) diff --git a/openpype/pipeline/publish/__init__.py b/openpype/pipeline/publish/__init__.py index ca958816fec..d106f28617b 100644 --- a/openpype/pipeline/publish/__init__.py +++ b/openpype/pipeline/publish/__init__.py @@ -6,7 +6,9 @@ from .lib import ( DiscoverResult, - publish_plugins_discover + publish_plugins_discover, + load_help_content_from_plugin, + load_help_content_from_filepath ) @@ -16,5 +18,7 @@ "OpenPypePyblishPluginMixin", "DiscoverResult", - "publish_plugins_discover" + "publish_plugins_discover", + "load_help_content_from_plugin", + "load_help_content_from_filepath" ) diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index 0fa712a3012..aa30ac22c97 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -1,6 +1,8 @@ import os import sys import types +import inspect +import xml.etree.ElementTree import six import pyblish.plugin @@ -28,6 +30,61 @@ def __setitem__(self, item, value): self.plugins[item] = value +class HelpContent: + def __init__(self, title, description, detail=None): + self.title = title + self.description = description + self.detail = detail + + +def load_help_content_from_filepath(filepath): + """Load help content from xml file. + + Xml file may containt errors and warnings. + """ + errors = {} + warnings = {} + output = { + "errors": errors, + "warnings": warnings + } + if not os.path.exists(filepath): + return output + tree = xml.etree.ElementTree.parse(filepath) + root = tree.getroot() + for child in root: + child_id = child.attrib.get("id") + if child_id is None: + continue + + # Make sure ID is string + child_id = str(child_id) + + title = child.find("title").text + description = child.find("description").text + detail_node = child.find("detail") + detail = None + if detail_node: + detail = detail_node.text + if child.tag == "error": + errors[child_id] = HelpContent(title, description, detail) + elif child.tag == "warning": + warnings[child_id] = HelpContent(title, description, detail) + return output + + +def load_help_content_from_plugin(plugin): + cls = plugin + if not inspect.isclass(plugin): + cls = plugin.__class__ + plugin_filepath = inspect.getfile(cls) + plugin_dir = os.path.dirname(plugin_filepath) + basename = os.path.splitext(os.path.basename(plugin_filepath))[0] + filename = basename + ".xml" + filepath = os.path.join(plugin_dir, "help", filename) + return load_help_content_from_filepath(filepath) + + def publish_plugins_discover(paths=None): """Find and return available pyblish plug-ins diff --git a/openpype/pipeline/publish/publish_plugins.py b/openpype/pipeline/publish/publish_plugins.py index b60b9f43a70..9a73d1acc61 100644 --- a/openpype/pipeline/publish/publish_plugins.py +++ b/openpype/pipeline/publish/publish_plugins.py @@ -1,3 +1,6 @@ +from .lib import load_help_content_from_plugin + + class PublishValidationError(Exception): """Validation error happened during publishing. @@ -12,13 +15,33 @@ class PublishValidationError(Exception): description(str): Detailed description of an error. It is possible to use Markdown syntax. """ - def __init__(self, message, title=None, description=None): + def __init__(self, message, title=None, description=None, detail=None): self.message = message self.title = title or "< Missing title >" self.description = description or message + self.detail = detail super(PublishValidationError, self).__init__(message) +class PublishXmlValidationError(PublishValidationError): + def __init__( + self, message, plugin, key=None, *formattings_arg, **formatting_kwargs + ): + if key is None: + key = "main" + result = load_help_content_from_plugin(plugin) + content_obj = result["errors"][key] + description = content_obj.description.format( + *formattings_arg, **formatting_kwargs + ) + detail = content_obj.detail.format( + *formattings_arg, **formatting_kwargs + ) + super(PublishXmlValidationError, self).__init__( + message, content_obj.title, description, detail + ) + + class KnownPublishError(Exception): """Publishing crashed because of known error. From c5b9a0bd05ecf8764b2418cdf1fbba0746f02cc2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 21 Dec 2021 13:22:11 +0100 Subject: [PATCH 287/492] added PublishXmlValidationError to 'pipeline.publish' --- openpype/pipeline/publish/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/pipeline/publish/__init__.py b/openpype/pipeline/publish/__init__.py index d106f28617b..228c4d8dcb4 100644 --- a/openpype/pipeline/publish/__init__.py +++ b/openpype/pipeline/publish/__init__.py @@ -1,5 +1,6 @@ from .publish_plugins import ( PublishValidationError, + PublishXmlValidationError, KnownPublishError, OpenPypePyblishPluginMixin ) @@ -14,6 +15,7 @@ __all__ = ( "PublishValidationError", + "PublishXmlValidationError", "KnownPublishError", "OpenPypePyblishPluginMixin", From d5b24781bb53234bf9942ab50d1760113d4f8fbd Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 21 Dec 2021 13:25:13 +0100 Subject: [PATCH 288/492] reduced formatting possibilities --- openpype/pipeline/publish/publish_plugins.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/openpype/pipeline/publish/publish_plugins.py b/openpype/pipeline/publish/publish_plugins.py index 9a73d1acc61..78dbaf2ddc6 100644 --- a/openpype/pipeline/publish/publish_plugins.py +++ b/openpype/pipeline/publish/publish_plugins.py @@ -25,18 +25,17 @@ def __init__(self, message, title=None, description=None, detail=None): class PublishXmlValidationError(PublishValidationError): def __init__( - self, message, plugin, key=None, *formattings_arg, **formatting_kwargs + self, message, plugin, key=None, formatting_data=None ): if key is None: key = "main" + + if not formatting_data: + formatting_data = {} result = load_help_content_from_plugin(plugin) content_obj = result["errors"][key] - description = content_obj.description.format( - *formattings_arg, **formatting_kwargs - ) - detail = content_obj.detail.format( - *formattings_arg, **formatting_kwargs - ) + description = content_obj.description.format(**formatting_data) + detail = content_obj.detail.format(**formatting_data) super(PublishXmlValidationError, self).__init__( message, content_obj.title, description, detail ) From a05a6785584e7efa5e6f93e9939c5e91b80038b2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 21 Dec 2021 13:50:05 +0100 Subject: [PATCH 289/492] imported PublishXmlValidationError to 'openpype.pipeline' level --- openpype/pipeline/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/pipeline/__init__.py b/openpype/pipeline/__init__.py index e968df4011e..79d6ce4d54f 100644 --- a/openpype/pipeline/__init__.py +++ b/openpype/pipeline/__init__.py @@ -9,6 +9,7 @@ from .publish import ( PublishValidationError, + PublishXmlValidationError, KnownPublishError, OpenPypePyblishPluginMixin ) @@ -23,6 +24,7 @@ "CreatedInstance", "PublishValidationError", + "PublishXmlValidationError", "KnownPublishError", "OpenPypePyblishPluginMixin" ) From fe3af18101de12804bc9361c5c121e4c4238aa74 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 21 Dec 2021 13:53:17 +0100 Subject: [PATCH 290/492] swapped plugin and message args order --- openpype/pipeline/publish/publish_plugins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/pipeline/publish/publish_plugins.py b/openpype/pipeline/publish/publish_plugins.py index 78dbaf2ddc6..48fa2499b81 100644 --- a/openpype/pipeline/publish/publish_plugins.py +++ b/openpype/pipeline/publish/publish_plugins.py @@ -25,7 +25,7 @@ def __init__(self, message, title=None, description=None, detail=None): class PublishXmlValidationError(PublishValidationError): def __init__( - self, message, plugin, key=None, formatting_data=None + self, plugin, message, key=None, formatting_data=None ): if key is None: key = "main" From 5683dda65d2f17ec17efc2d4f749d95324295236 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 21 Dec 2021 14:05:12 +0100 Subject: [PATCH 291/492] do not format empty detail --- openpype/pipeline/publish/publish_plugins.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/pipeline/publish/publish_plugins.py b/openpype/pipeline/publish/publish_plugins.py index 48fa2499b81..bce64ec7096 100644 --- a/openpype/pipeline/publish/publish_plugins.py +++ b/openpype/pipeline/publish/publish_plugins.py @@ -35,7 +35,9 @@ def __init__( result = load_help_content_from_plugin(plugin) content_obj = result["errors"][key] description = content_obj.description.format(**formatting_data) - detail = content_obj.detail.format(**formatting_data) + detail = content_obj.detail + if detail: + detail = detail.format(**formatting_data) super(PublishXmlValidationError, self).__init__( message, content_obj.title, description, detail ) From d6b386583633c0c510f6b54a6b0999f7312f8e75 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 21 Dec 2021 14:06:27 +0100 Subject: [PATCH 292/492] fix detail node bool --- openpype/pipeline/publish/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index aa30ac22c97..f38e73afe2e 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -64,7 +64,7 @@ def load_help_content_from_filepath(filepath): description = child.find("description").text detail_node = child.find("detail") detail = None - if detail_node: + if detail_node is not None: detail = detail_node.text if child.tag == "error": errors[child_id] = HelpContent(title, description, detail) From 0c7a12c1f22938704c3c70bdd9ee375a8c5366d5 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Dec 2021 15:59:00 +0100 Subject: [PATCH 293/492] Implemented validators for New publisher for Photoshop --- .../publish/help/validate_instance_asset.xml | 25 +++++++++++++++++++ .../plugins/publish/help/validate_naming.xml | 21 ++++++++++++++++ .../publish/help/validate_unique_subsets.xml | 23 +++++++++++++++++ .../publish/validate_instance_asset.py | 12 +++++++-- .../plugins/publish/validate_naming.py | 24 +++++++++++------- .../publish/validate_unique_subsets.py | 23 +++++++++++++---- 6 files changed, 112 insertions(+), 16 deletions(-) create mode 100644 openpype/hosts/photoshop/plugins/publish/help/validate_instance_asset.xml create mode 100644 openpype/hosts/photoshop/plugins/publish/help/validate_naming.xml create mode 100644 openpype/hosts/photoshop/plugins/publish/help/validate_unique_subsets.xml diff --git a/openpype/hosts/photoshop/plugins/publish/help/validate_instance_asset.xml b/openpype/hosts/photoshop/plugins/publish/help/validate_instance_asset.xml new file mode 100644 index 00000000000..3b040e8ea8e --- /dev/null +++ b/openpype/hosts/photoshop/plugins/publish/help/validate_instance_asset.xml @@ -0,0 +1,25 @@ + + + +Subset context + +## Invalid subset context + +Asset name found '{found}' in subsets, expected '{expected}'. + +### How to repair? + +You can fix this with `Repair` button on the right. This will use '{expected}' asset name and overwrite '{found}' asset name in scene metadata. + +After that restart `Publish` with a `Reload button`. + +If this is unwanted, close workfile and open again, that way different asset value would be used for context information. + + +### __Detailed Info__ (optional) + +This might happen if you are reuse old workfile and open it in different context. +(Eg. you created subset "renderCompositingDefault" from asset "Robot' in "your_project_Robot_compositing.aep", now you opened this workfile in a context "Sloth" but existing subset for "Robot" asset stayed in the workfile.) + + + \ No newline at end of file diff --git a/openpype/hosts/photoshop/plugins/publish/help/validate_naming.xml b/openpype/hosts/photoshop/plugins/publish/help/validate_naming.xml new file mode 100644 index 00000000000..21a73703406 --- /dev/null +++ b/openpype/hosts/photoshop/plugins/publish/help/validate_naming.xml @@ -0,0 +1,21 @@ + + + +Invalid name + +## Invalid name of subset + +Name of subset is created from a layer name. Some characters (whitespace, '/' etc.) are not allowed because of publishing (files couldn't be saved on some OSes). + +### How to repair? + +You can fix this with `Repair` button on the right. This will remove invalid characters with safe character ('_' by default) in both subset names and matching group names. + +After that restart `Publish` with a `Reload button`. + +Or you use `Subset Manager` to delete existing subsets, remove created groups, rename layers that are used for their creation and use `Create` option in the Openpype menu to create them again. + +Invalid characters and 'safe character' could be configured in Settings. Ask your OpenPype admin to modify them if necessary. + + + \ No newline at end of file diff --git a/openpype/hosts/photoshop/plugins/publish/help/validate_unique_subsets.xml b/openpype/hosts/photoshop/plugins/publish/help/validate_unique_subsets.xml new file mode 100644 index 00000000000..fa7c76a2ddf --- /dev/null +++ b/openpype/hosts/photoshop/plugins/publish/help/validate_unique_subsets.xml @@ -0,0 +1,23 @@ + + + +Subsets duplicated + +## Some subsets are duplicated + +Created subsets must be unique. + +Subsets '{duplicates_str}' are duplicated. + +### How to repair? + +Use `Subset Manager` to delete duplicated subset to have only unique subset names and restart `Publish` with a `Reload button`. + + +### __Detailed Info__ (optional) + +Subset names are created from layer names. Layer names are filtered for characters that would break publishing process when files are created. +This replacement process might result in duplicate names of subsets. + + + \ No newline at end of file diff --git a/openpype/hosts/photoshop/plugins/publish/validate_instance_asset.py b/openpype/hosts/photoshop/plugins/publish/validate_instance_asset.py index 4dc1972074c..8f13cc6b332 100644 --- a/openpype/hosts/photoshop/plugins/publish/validate_instance_asset.py +++ b/openpype/hosts/photoshop/plugins/publish/validate_instance_asset.py @@ -1,8 +1,10 @@ from avalon import api import pyblish.api -import openpype.api from avalon import photoshop +import openpype.api +from openpype.pipeline import PublishXmlValidationError + class ValidateInstanceAssetRepair(pyblish.api.Action): """Repair the instance asset.""" @@ -56,4 +58,10 @@ def process(self, instance): f"If that's not correct value, close workfile and " f"reopen via Workfiles!" ) - assert instance_asset == current_asset, msg + formatting_data = { + "found": instance_asset, + "expected": current_asset + } + if instance_asset != current_asset: + raise PublishXmlValidationError(self, msg, + formatting_data=formatting_data) diff --git a/openpype/hosts/photoshop/plugins/publish/validate_naming.py b/openpype/hosts/photoshop/plugins/publish/validate_naming.py index 1635096f4b2..d548992f094 100644 --- a/openpype/hosts/photoshop/plugins/publish/validate_naming.py +++ b/openpype/hosts/photoshop/plugins/publish/validate_naming.py @@ -1,9 +1,11 @@ import re import pyblish.api -import openpype.api from avalon import photoshop +import openpype.api +from openpype.pipeline import PublishXmlValidationError + class ValidateNamingRepair(pyblish.api.Action): """Repair the instance asset.""" @@ -69,14 +71,18 @@ class ValidateNaming(pyblish.api.InstancePlugin): replace_char = '' def process(self, instance): - help_msg = ' Use Repair action (A) in Pyblish to fix it.' - msg = "Name \"{}\" is not allowed.{}".format(instance.data["name"], - help_msg) - assert not re.search(self.invalid_chars, instance.data["name"]), msg - - msg = "Subset \"{}\" is not allowed.{}".format(instance.data["subset"], - help_msg) - assert not re.search(self.invalid_chars, instance.data["subset"]), msg + msg = "Name \"{}\" is not allowed.".format(instance.data["name"]) + + formatting_data = {"error_msg": msg} + if re.search(self.invalid_chars, instance.data["name"]): + raise PublishXmlValidationError(self, msg, + formatting_data=formatting_data) + + msg = "Subset \"{}\" is not allowed.".format(instance.data["subset"]) + formatting_data = {"error_msg": msg} + if re.search(self.invalid_chars, instance.data["subset"]): + raise PublishXmlValidationError(self, msg, + formatting_data=formatting_data) @classmethod def get_replace_chars(cls): diff --git a/openpype/hosts/photoshop/plugins/publish/validate_unique_subsets.py b/openpype/hosts/photoshop/plugins/publish/validate_unique_subsets.py index 15ae5fbcea2..d41fefa9710 100644 --- a/openpype/hosts/photoshop/plugins/publish/validate_unique_subsets.py +++ b/openpype/hosts/photoshop/plugins/publish/validate_unique_subsets.py @@ -1,5 +1,8 @@ +import collections + import pyblish.api import openpype.api +from openpype.pipeline import PublishXmlValidationError class ValidateSubsetUniqueness(pyblish.api.ContextPlugin): @@ -19,8 +22,18 @@ def process(self, context): if instance.data.get('publish'): subset_names.append(instance.data.get('subset')) - msg = ( - "Instance subset names are not unique. " + - "Remove duplicates via SubsetManager." - ) - assert len(subset_names) == len(set(subset_names)), msg + duplicates = [item + for item, count in + collections.Counter(subset_names).items() + if count > 1] + + if duplicates: + duplicates_str = ",".join(duplicates) + formatting_data = {"duplicates_str": duplicates_str} + msg = ( + "Instance subset names {} are not unique.".format( + duplicates_str) + + " Remove duplicates via SubsetManager." + ) + raise PublishXmlValidationError(self, msg, + formatting_data=formatting_data) From 96da6a07f612d0a1357f4e46640919b38001d750 Mon Sep 17 00:00:00 2001 From: OpenPype Date: Wed, 22 Dec 2021 03:42:14 +0000 Subject: [PATCH 294/492] [Automated] Bump version --- CHANGELOG.md | 20 +++++--------------- openpype/version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 7 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1eb8455a095..0c6d1b8fe1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## [3.7.0-nightly.9](https://github.com/pypeclub/OpenPype/tree/HEAD) +## [3.7.0-nightly.10](https://github.com/pypeclub/OpenPype/tree/HEAD) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.6.4...HEAD) @@ -8,14 +8,12 @@ - General: Default modules hierarchy n2 [\#2368](https://github.com/pypeclub/OpenPype/pull/2368) -**🆕 New features** - -- Settings UI use OpenPype styles [\#2296](https://github.com/pypeclub/OpenPype/pull/2296) - **🚀 Enhancements** +- General: Environment variables groups [\#2424](https://github.com/pypeclub/OpenPype/pull/2424) - Settings UI: Hyperlinks to settings [\#2420](https://github.com/pypeclub/OpenPype/pull/2420) - Modules: JobQueue module moved one hierarchy level higher [\#2419](https://github.com/pypeclub/OpenPype/pull/2419) +- TimersManager: Start timer post launch hook [\#2418](https://github.com/pypeclub/OpenPype/pull/2418) - Ftrack: Check existence of object type on recreation [\#2404](https://github.com/pypeclub/OpenPype/pull/2404) - Flame: moving `utility\_scripts` to api folder also with `scripts` [\#2385](https://github.com/pypeclub/OpenPype/pull/2385) - Centos 7 dependency compatibility [\#2384](https://github.com/pypeclub/OpenPype/pull/2384) @@ -27,18 +25,14 @@ - Burnins: Be able recognize mxf OPAtom format [\#2361](https://github.com/pypeclub/OpenPype/pull/2361) - Maya: Add is\_static\_image\_plane and is\_in\_all\_views option in imagePlaneLoader [\#2356](https://github.com/pypeclub/OpenPype/pull/2356) - Local settings: Copyable studio paths [\#2349](https://github.com/pypeclub/OpenPype/pull/2349) -- Assets Widget: Clear model on project change [\#2345](https://github.com/pypeclub/OpenPype/pull/2345) -- General: OpenPype default modules hierarchy [\#2338](https://github.com/pypeclub/OpenPype/pull/2338) -- General: FFprobe error exception contain original error message [\#2328](https://github.com/pypeclub/OpenPype/pull/2328) -- Resolve: Add experimental button to menu [\#2325](https://github.com/pypeclub/OpenPype/pull/2325) -- General: Reduce vendor imports [\#2305](https://github.com/pypeclub/OpenPype/pull/2305) -- Ftrack: Synchronize input links [\#2287](https://github.com/pypeclub/OpenPype/pull/2287) +- General: Multilayer EXRs support [\#2315](https://github.com/pypeclub/OpenPype/pull/2315) **🐛 Bug fixes** - PS: Introduced settings for invalid characters to use in ValidateNaming plugin [\#2417](https://github.com/pypeclub/OpenPype/pull/2417) - Settings UI: Breadcrumbs path does not create new entities [\#2416](https://github.com/pypeclub/OpenPype/pull/2416) - AfterEffects: Variant 2022 is in defaults but missing in schemas [\#2412](https://github.com/pypeclub/OpenPype/pull/2412) +- Nuke: baking representations was not additive [\#2406](https://github.com/pypeclub/OpenPype/pull/2406) - General: Fix access to environments from default settings [\#2403](https://github.com/pypeclub/OpenPype/pull/2403) - Fix: Placeholder Input color set fix [\#2399](https://github.com/pypeclub/OpenPype/pull/2399) - Settings: Fix state change of wrapper label [\#2396](https://github.com/pypeclub/OpenPype/pull/2396) @@ -53,12 +47,9 @@ - Launcher: Minimize button on MacOs [\#2355](https://github.com/pypeclub/OpenPype/pull/2355) - StandalonePublisher: Fix import of constant [\#2354](https://github.com/pypeclub/OpenPype/pull/2354) - Adobe products show issue [\#2347](https://github.com/pypeclub/OpenPype/pull/2347) -- Maya Look Assigner: Fix Python 3 compatibility [\#2343](https://github.com/pypeclub/OpenPype/pull/2343) - Remove wrongly used host for hook [\#2342](https://github.com/pypeclub/OpenPype/pull/2342) - Tools: Use Qt context on tools show [\#2340](https://github.com/pypeclub/OpenPype/pull/2340) - Flame: Fix default argument value in custom dictionary [\#2339](https://github.com/pypeclub/OpenPype/pull/2339) -- Timers Manager: Disable auto stop timer on linux platform [\#2334](https://github.com/pypeclub/OpenPype/pull/2334) -- Fix - provider icons are pulled from a folder [\#2326](https://github.com/pypeclub/OpenPype/pull/2326) - Royal Render: Fix plugin order and OpenPype auto-detection [\#2291](https://github.com/pypeclub/OpenPype/pull/2291) **Merged pull requests:** @@ -67,7 +58,6 @@ - Add validator to check correct version of extension for PS and AE [\#2387](https://github.com/pypeclub/OpenPype/pull/2387) - Linux : flip updating submodules logic [\#2357](https://github.com/pypeclub/OpenPype/pull/2357) - Update of avalon-core [\#2346](https://github.com/pypeclub/OpenPype/pull/2346) -- Maya: configurable model top level validation [\#2321](https://github.com/pypeclub/OpenPype/pull/2321) ## [3.6.4](https://github.com/pypeclub/OpenPype/tree/3.6.4) (2021-11-23) diff --git a/openpype/version.py b/openpype/version.py index 544160d41c9..273755dfd03 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.7.0-nightly.9" +__version__ = "3.7.0-nightly.10" diff --git a/pyproject.toml b/pyproject.toml index 07a9ac8e433..ea6d9ee5e56 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.7.0-nightly.9" # OpenPype +version = "3.7.0-nightly.10" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From 5efef322a6415aa0ebb4c985a00e26d1341ab9e0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 11:37:31 +0100 Subject: [PATCH 295/492] added attribute log_mongo_url_components back for older builds --- openpype/lib/log.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openpype/lib/log.py b/openpype/lib/log.py index a34cb898e32..a42faef0081 100644 --- a/openpype/lib/log.py +++ b/openpype/lib/log.py @@ -202,6 +202,11 @@ class PypeLogger: use_mongo_logging = None mongo_process_id = None + # Backwards compatibility - was used in start.py + # TODO remove when all old builds are replaced with new one + # not using 'log_mongo_url_components' + log_mongo_url_components = None + # Database name in Mongo log_database_name = os.environ["OPENPYPE_DATABASE_NAME"] # Collection name under database in Mongo @@ -320,6 +325,7 @@ def _initialize(cls): # Change initialization state to prevent runtime changes # if is executed during runtime cls.initialized = False + cls.log_mongo_url_components = get_default_components() # Define if should logging to mongo be used use_mongo_logging = bool(log4mongo is not None) From 1914c144cd2998dc40c5253f8ea9597648d51ad5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 11:38:43 +0100 Subject: [PATCH 296/492] uses get_default_components for log components --- start.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/start.py b/start.py index ae6aefe34ee..b5a8d95fd06 100644 --- a/start.py +++ b/start.py @@ -1109,15 +1109,15 @@ def get_info(use_staging=None) -> list: # Reinitialize PypeLogger.initialize() - log_components = PypeLogger.log_mongo_url_components - if log_components["host"]: - inf.append(("Logging to MongoDB", log_components["host"])) - inf.append((" - port", log_components["port"] or "")) + mongo_components = get_default_components() + if mongo_components["host"]: + inf.append(("Logging to MongoDB", mongo_components["host"])) + inf.append((" - port", mongo_components["port"] or "")) inf.append((" - database", PypeLogger.log_database_name)) inf.append((" - collection", PypeLogger.log_collection_name)) - inf.append((" - user", log_components["username"] or "")) - if log_components["auth_db"]: - inf.append((" - auth source", log_components["auth_db"])) + inf.append((" - user", mongo_components["username"] or "")) + if mongo_components["auth_db"]: + inf.append((" - auth source", mongo_components["auth_db"])) maximum = max(len(i[0]) for i in inf) formatted = [] From beeea44cb952412b1e4b1d78f0e78340aa5c68c3 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 22 Dec 2021 13:09:14 +0100 Subject: [PATCH 297/492] Forced cx_freeze to include sqlite3 into build because of pytest requirements --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index cd3ed4f82cb..a21645e66ac 100644 --- a/setup.py +++ b/setup.py @@ -48,7 +48,8 @@ "filecmp", "dns", # Python defaults (cx_Freeze skip them by default) - "dbm" + "dbm", + "sqlite3" ] includes = [] From 8d06754ba286cefb80646c6d3d29577ba00182db Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 15:51:42 +0100 Subject: [PATCH 298/492] update avalon unreal plugin --- repos/avalon-unreal-integration | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repos/avalon-unreal-integration b/repos/avalon-unreal-integration index 43f6ea94398..8529332aedd 160000 --- a/repos/avalon-unreal-integration +++ b/repos/avalon-unreal-integration @@ -1 +1 @@ -Subproject commit 43f6ea943980b29c02a170942b566ae11f2b7080 +Subproject commit 8529332aeddddc0bfccc7b1455ce0fa0aa571da9 From 5dedd2655dd501d5ad6aa59161b54fba548768d4 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 22 Dec 2021 16:40:20 +0100 Subject: [PATCH 299/492] Revert "Photoshop: New style validations for New publisher" --- .../publish/help/validate_instance_asset.xml | 25 -------- .../plugins/publish/help/validate_naming.xml | 21 ------- .../publish/help/validate_unique_subsets.xml | 23 -------- .../publish/validate_instance_asset.py | 12 +--- .../plugins/publish/validate_naming.py | 24 +++----- .../publish/validate_unique_subsets.py | 23 ++------ openpype/pipeline/__init__.py | 2 - openpype/pipeline/publish/__init__.py | 10 +--- openpype/pipeline/publish/lib.py | 57 ------------------- openpype/pipeline/publish/publish_plugins.py | 26 +-------- 10 files changed, 19 insertions(+), 204 deletions(-) delete mode 100644 openpype/hosts/photoshop/plugins/publish/help/validate_instance_asset.xml delete mode 100644 openpype/hosts/photoshop/plugins/publish/help/validate_naming.xml delete mode 100644 openpype/hosts/photoshop/plugins/publish/help/validate_unique_subsets.xml diff --git a/openpype/hosts/photoshop/plugins/publish/help/validate_instance_asset.xml b/openpype/hosts/photoshop/plugins/publish/help/validate_instance_asset.xml deleted file mode 100644 index 3b040e8ea8e..00000000000 --- a/openpype/hosts/photoshop/plugins/publish/help/validate_instance_asset.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - -Subset context - -## Invalid subset context - -Asset name found '{found}' in subsets, expected '{expected}'. - -### How to repair? - -You can fix this with `Repair` button on the right. This will use '{expected}' asset name and overwrite '{found}' asset name in scene metadata. - -After that restart `Publish` with a `Reload button`. - -If this is unwanted, close workfile and open again, that way different asset value would be used for context information. - - -### __Detailed Info__ (optional) - -This might happen if you are reuse old workfile and open it in different context. -(Eg. you created subset "renderCompositingDefault" from asset "Robot' in "your_project_Robot_compositing.aep", now you opened this workfile in a context "Sloth" but existing subset for "Robot" asset stayed in the workfile.) - - - \ No newline at end of file diff --git a/openpype/hosts/photoshop/plugins/publish/help/validate_naming.xml b/openpype/hosts/photoshop/plugins/publish/help/validate_naming.xml deleted file mode 100644 index 21a73703406..00000000000 --- a/openpype/hosts/photoshop/plugins/publish/help/validate_naming.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - -Invalid name - -## Invalid name of subset - -Name of subset is created from a layer name. Some characters (whitespace, '/' etc.) are not allowed because of publishing (files couldn't be saved on some OSes). - -### How to repair? - -You can fix this with `Repair` button on the right. This will remove invalid characters with safe character ('_' by default) in both subset names and matching group names. - -After that restart `Publish` with a `Reload button`. - -Or you use `Subset Manager` to delete existing subsets, remove created groups, rename layers that are used for their creation and use `Create` option in the Openpype menu to create them again. - -Invalid characters and 'safe character' could be configured in Settings. Ask your OpenPype admin to modify them if necessary. - - - \ No newline at end of file diff --git a/openpype/hosts/photoshop/plugins/publish/help/validate_unique_subsets.xml b/openpype/hosts/photoshop/plugins/publish/help/validate_unique_subsets.xml deleted file mode 100644 index fa7c76a2ddf..00000000000 --- a/openpype/hosts/photoshop/plugins/publish/help/validate_unique_subsets.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - -Subsets duplicated - -## Some subsets are duplicated - -Created subsets must be unique. - -Subsets '{duplicates_str}' are duplicated. - -### How to repair? - -Use `Subset Manager` to delete duplicated subset to have only unique subset names and restart `Publish` with a `Reload button`. - - -### __Detailed Info__ (optional) - -Subset names are created from layer names. Layer names are filtered for characters that would break publishing process when files are created. -This replacement process might result in duplicate names of subsets. - - - \ No newline at end of file diff --git a/openpype/hosts/photoshop/plugins/publish/validate_instance_asset.py b/openpype/hosts/photoshop/plugins/publish/validate_instance_asset.py index 8f13cc6b332..4dc1972074c 100644 --- a/openpype/hosts/photoshop/plugins/publish/validate_instance_asset.py +++ b/openpype/hosts/photoshop/plugins/publish/validate_instance_asset.py @@ -1,9 +1,7 @@ from avalon import api import pyblish.api -from avalon import photoshop - import openpype.api -from openpype.pipeline import PublishXmlValidationError +from avalon import photoshop class ValidateInstanceAssetRepair(pyblish.api.Action): @@ -58,10 +56,4 @@ def process(self, instance): f"If that's not correct value, close workfile and " f"reopen via Workfiles!" ) - formatting_data = { - "found": instance_asset, - "expected": current_asset - } - if instance_asset != current_asset: - raise PublishXmlValidationError(self, msg, - formatting_data=formatting_data) + assert instance_asset == current_asset, msg diff --git a/openpype/hosts/photoshop/plugins/publish/validate_naming.py b/openpype/hosts/photoshop/plugins/publish/validate_naming.py index d548992f094..1635096f4b2 100644 --- a/openpype/hosts/photoshop/plugins/publish/validate_naming.py +++ b/openpype/hosts/photoshop/plugins/publish/validate_naming.py @@ -1,10 +1,8 @@ import re import pyblish.api -from avalon import photoshop - import openpype.api -from openpype.pipeline import PublishXmlValidationError +from avalon import photoshop class ValidateNamingRepair(pyblish.api.Action): @@ -71,18 +69,14 @@ class ValidateNaming(pyblish.api.InstancePlugin): replace_char = '' def process(self, instance): - msg = "Name \"{}\" is not allowed.".format(instance.data["name"]) - - formatting_data = {"error_msg": msg} - if re.search(self.invalid_chars, instance.data["name"]): - raise PublishXmlValidationError(self, msg, - formatting_data=formatting_data) - - msg = "Subset \"{}\" is not allowed.".format(instance.data["subset"]) - formatting_data = {"error_msg": msg} - if re.search(self.invalid_chars, instance.data["subset"]): - raise PublishXmlValidationError(self, msg, - formatting_data=formatting_data) + help_msg = ' Use Repair action (A) in Pyblish to fix it.' + msg = "Name \"{}\" is not allowed.{}".format(instance.data["name"], + help_msg) + assert not re.search(self.invalid_chars, instance.data["name"]), msg + + msg = "Subset \"{}\" is not allowed.{}".format(instance.data["subset"], + help_msg) + assert not re.search(self.invalid_chars, instance.data["subset"]), msg @classmethod def get_replace_chars(cls): diff --git a/openpype/hosts/photoshop/plugins/publish/validate_unique_subsets.py b/openpype/hosts/photoshop/plugins/publish/validate_unique_subsets.py index d41fefa9710..15ae5fbcea2 100644 --- a/openpype/hosts/photoshop/plugins/publish/validate_unique_subsets.py +++ b/openpype/hosts/photoshop/plugins/publish/validate_unique_subsets.py @@ -1,8 +1,5 @@ -import collections - import pyblish.api import openpype.api -from openpype.pipeline import PublishXmlValidationError class ValidateSubsetUniqueness(pyblish.api.ContextPlugin): @@ -22,18 +19,8 @@ def process(self, context): if instance.data.get('publish'): subset_names.append(instance.data.get('subset')) - duplicates = [item - for item, count in - collections.Counter(subset_names).items() - if count > 1] - - if duplicates: - duplicates_str = ",".join(duplicates) - formatting_data = {"duplicates_str": duplicates_str} - msg = ( - "Instance subset names {} are not unique.".format( - duplicates_str) + - " Remove duplicates via SubsetManager." - ) - raise PublishXmlValidationError(self, msg, - formatting_data=formatting_data) + msg = ( + "Instance subset names are not unique. " + + "Remove duplicates via SubsetManager." + ) + assert len(subset_names) == len(set(subset_names)), msg diff --git a/openpype/pipeline/__init__.py b/openpype/pipeline/__init__.py index 79d6ce4d54f..e968df4011e 100644 --- a/openpype/pipeline/__init__.py +++ b/openpype/pipeline/__init__.py @@ -9,7 +9,6 @@ from .publish import ( PublishValidationError, - PublishXmlValidationError, KnownPublishError, OpenPypePyblishPluginMixin ) @@ -24,7 +23,6 @@ "CreatedInstance", "PublishValidationError", - "PublishXmlValidationError", "KnownPublishError", "OpenPypePyblishPluginMixin" ) diff --git a/openpype/pipeline/publish/__init__.py b/openpype/pipeline/publish/__init__.py index 228c4d8dcb4..ca958816fec 100644 --- a/openpype/pipeline/publish/__init__.py +++ b/openpype/pipeline/publish/__init__.py @@ -1,26 +1,20 @@ from .publish_plugins import ( PublishValidationError, - PublishXmlValidationError, KnownPublishError, OpenPypePyblishPluginMixin ) from .lib import ( DiscoverResult, - publish_plugins_discover, - load_help_content_from_plugin, - load_help_content_from_filepath + publish_plugins_discover ) __all__ = ( "PublishValidationError", - "PublishXmlValidationError", "KnownPublishError", "OpenPypePyblishPluginMixin", "DiscoverResult", - "publish_plugins_discover", - "load_help_content_from_plugin", - "load_help_content_from_filepath" + "publish_plugins_discover" ) diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index f38e73afe2e..0fa712a3012 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -1,8 +1,6 @@ import os import sys import types -import inspect -import xml.etree.ElementTree import six import pyblish.plugin @@ -30,61 +28,6 @@ def __setitem__(self, item, value): self.plugins[item] = value -class HelpContent: - def __init__(self, title, description, detail=None): - self.title = title - self.description = description - self.detail = detail - - -def load_help_content_from_filepath(filepath): - """Load help content from xml file. - - Xml file may containt errors and warnings. - """ - errors = {} - warnings = {} - output = { - "errors": errors, - "warnings": warnings - } - if not os.path.exists(filepath): - return output - tree = xml.etree.ElementTree.parse(filepath) - root = tree.getroot() - for child in root: - child_id = child.attrib.get("id") - if child_id is None: - continue - - # Make sure ID is string - child_id = str(child_id) - - title = child.find("title").text - description = child.find("description").text - detail_node = child.find("detail") - detail = None - if detail_node is not None: - detail = detail_node.text - if child.tag == "error": - errors[child_id] = HelpContent(title, description, detail) - elif child.tag == "warning": - warnings[child_id] = HelpContent(title, description, detail) - return output - - -def load_help_content_from_plugin(plugin): - cls = plugin - if not inspect.isclass(plugin): - cls = plugin.__class__ - plugin_filepath = inspect.getfile(cls) - plugin_dir = os.path.dirname(plugin_filepath) - basename = os.path.splitext(os.path.basename(plugin_filepath))[0] - filename = basename + ".xml" - filepath = os.path.join(plugin_dir, "help", filename) - return load_help_content_from_filepath(filepath) - - def publish_plugins_discover(paths=None): """Find and return available pyblish plug-ins diff --git a/openpype/pipeline/publish/publish_plugins.py b/openpype/pipeline/publish/publish_plugins.py index bce64ec7096..b60b9f43a70 100644 --- a/openpype/pipeline/publish/publish_plugins.py +++ b/openpype/pipeline/publish/publish_plugins.py @@ -1,6 +1,3 @@ -from .lib import load_help_content_from_plugin - - class PublishValidationError(Exception): """Validation error happened during publishing. @@ -15,34 +12,13 @@ class PublishValidationError(Exception): description(str): Detailed description of an error. It is possible to use Markdown syntax. """ - def __init__(self, message, title=None, description=None, detail=None): + def __init__(self, message, title=None, description=None): self.message = message self.title = title or "< Missing title >" self.description = description or message - self.detail = detail super(PublishValidationError, self).__init__(message) -class PublishXmlValidationError(PublishValidationError): - def __init__( - self, plugin, message, key=None, formatting_data=None - ): - if key is None: - key = "main" - - if not formatting_data: - formatting_data = {} - result = load_help_content_from_plugin(plugin) - content_obj = result["errors"][key] - description = content_obj.description.format(**formatting_data) - detail = content_obj.detail - if detail: - detail = detail.format(**formatting_data) - super(PublishXmlValidationError, self).__init__( - message, content_obj.title, description, detail - ) - - class KnownPublishError(Exception): """Publishing crashed because of known error. From 86bdddf343513fb34aefcceaad2d1a50b0643e4c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 18:30:40 +0100 Subject: [PATCH 300/492] renamed linux_app_launcher to app_launcher --- linux_app_launcher.py => app_launcher.py | 0 openpype/lib/execute.py | 2 +- setup.py | 4 ++-- 3 files changed, 3 insertions(+), 3 deletions(-) rename linux_app_launcher.py => app_launcher.py (100%) diff --git a/linux_app_launcher.py b/app_launcher.py similarity index 100% rename from linux_app_launcher.py rename to app_launcher.py diff --git a/openpype/lib/execute.py b/openpype/lib/execute.py index a36a1f0bf48..f97617d906f 100644 --- a/openpype/lib/execute.py +++ b/openpype/lib/execute.py @@ -193,7 +193,7 @@ def get_linux_launcher_args(*args): list: Executables with possible positional argument to script when called from code. """ - filename = "linux_app_launcher" + filename = "app_launcher" openpype_executable = os.environ["OPENPYPE_EXECUTABLE"] executable_filename = os.path.basename(openpype_executable) diff --git a/setup.py b/setup.py index de4887ae3c5..a16984ca8e0 100644 --- a/setup.py +++ b/setup.py @@ -112,9 +112,9 @@ if IS_LINUX: executables.append( Executable( - "linux_app_launcher.py", + "app_launcher.py", base=None, - target_name="linux_app_launcher", + target_name="app_launcher", icon=icon_path.as_posix() ) ) From 6dabbf26014d2abb5eed4406085de9ffa8060511 Mon Sep 17 00:00:00 2001 From: OpenPype Date: Sat, 25 Dec 2021 03:42:38 +0000 Subject: [PATCH 301/492] [Automated] Bump version --- CHANGELOG.md | 27 +++++++++------------------ openpype/version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 11 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c6d1b8fe1c..6b7b5ab20fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## [3.7.0-nightly.10](https://github.com/pypeclub/OpenPype/tree/HEAD) +## [3.7.0-nightly.11](https://github.com/pypeclub/OpenPype/tree/HEAD) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.6.4...HEAD) @@ -10,11 +10,15 @@ **🚀 Enhancements** +- Photoshop: New style validations for New publisher [\#2429](https://github.com/pypeclub/OpenPype/pull/2429) - General: Environment variables groups [\#2424](https://github.com/pypeclub/OpenPype/pull/2424) +- Unreal: Dynamic menu created in Python [\#2422](https://github.com/pypeclub/OpenPype/pull/2422) - Settings UI: Hyperlinks to settings [\#2420](https://github.com/pypeclub/OpenPype/pull/2420) - Modules: JobQueue module moved one hierarchy level higher [\#2419](https://github.com/pypeclub/OpenPype/pull/2419) - TimersManager: Start timer post launch hook [\#2418](https://github.com/pypeclub/OpenPype/pull/2418) - Ftrack: Check existence of object type on recreation [\#2404](https://github.com/pypeclub/OpenPype/pull/2404) +- Enhancement: Global cleanup plugin that explicitly remove paths from context [\#2402](https://github.com/pypeclub/OpenPype/pull/2402) +- General: MongoDB ability to specify replica set groups [\#2401](https://github.com/pypeclub/OpenPype/pull/2401) - Flame: moving `utility\_scripts` to api folder also with `scripts` [\#2385](https://github.com/pypeclub/OpenPype/pull/2385) - Centos 7 dependency compatibility [\#2384](https://github.com/pypeclub/OpenPype/pull/2384) - Enhancement: Settings: Use project settings values from another project [\#2382](https://github.com/pypeclub/OpenPype/pull/2382) @@ -24,11 +28,10 @@ - Hiero: python3 compatibility [\#2365](https://github.com/pypeclub/OpenPype/pull/2365) - Burnins: Be able recognize mxf OPAtom format [\#2361](https://github.com/pypeclub/OpenPype/pull/2361) - Maya: Add is\_static\_image\_plane and is\_in\_all\_views option in imagePlaneLoader [\#2356](https://github.com/pypeclub/OpenPype/pull/2356) -- Local settings: Copyable studio paths [\#2349](https://github.com/pypeclub/OpenPype/pull/2349) -- General: Multilayer EXRs support [\#2315](https://github.com/pypeclub/OpenPype/pull/2315) **🐛 Bug fixes** +- Short Pyblish plugin path [\#2428](https://github.com/pypeclub/OpenPype/pull/2428) - PS: Introduced settings for invalid characters to use in ValidateNaming plugin [\#2417](https://github.com/pypeclub/OpenPype/pull/2417) - Settings UI: Breadcrumbs path does not create new entities [\#2416](https://github.com/pypeclub/OpenPype/pull/2416) - AfterEffects: Variant 2022 is in defaults but missing in schemas [\#2412](https://github.com/pypeclub/OpenPype/pull/2412) @@ -44,37 +47,25 @@ - Nuke: fixing node name based on switched asset name [\#2369](https://github.com/pypeclub/OpenPype/pull/2369) - JobQueue: Fix loading of settings [\#2362](https://github.com/pypeclub/OpenPype/pull/2362) - Tools: Placeholder color [\#2359](https://github.com/pypeclub/OpenPype/pull/2359) -- Launcher: Minimize button on MacOs [\#2355](https://github.com/pypeclub/OpenPype/pull/2355) -- StandalonePublisher: Fix import of constant [\#2354](https://github.com/pypeclub/OpenPype/pull/2354) -- Adobe products show issue [\#2347](https://github.com/pypeclub/OpenPype/pull/2347) -- Remove wrongly used host for hook [\#2342](https://github.com/pypeclub/OpenPype/pull/2342) -- Tools: Use Qt context on tools show [\#2340](https://github.com/pypeclub/OpenPype/pull/2340) +- Houdini: Fix HDA creation [\#2350](https://github.com/pypeclub/OpenPype/pull/2350) - Flame: Fix default argument value in custom dictionary [\#2339](https://github.com/pypeclub/OpenPype/pull/2339) -- Royal Render: Fix plugin order and OpenPype auto-detection [\#2291](https://github.com/pypeclub/OpenPype/pull/2291) **Merged pull requests:** +- Forced cx\_freeze to include sqlite3 into build [\#2432](https://github.com/pypeclub/OpenPype/pull/2432) +- Maya: Replaced PATH usage with vendored oiio path for maketx utility [\#2405](https://github.com/pypeclub/OpenPype/pull/2405) - \[Fix\]\[MAYA\] Handle message type attribute within CollectLook [\#2394](https://github.com/pypeclub/OpenPype/pull/2394) - Add validator to check correct version of extension for PS and AE [\#2387](https://github.com/pypeclub/OpenPype/pull/2387) - Linux : flip updating submodules logic [\#2357](https://github.com/pypeclub/OpenPype/pull/2357) -- Update of avalon-core [\#2346](https://github.com/pypeclub/OpenPype/pull/2346) ## [3.6.4](https://github.com/pypeclub/OpenPype/tree/3.6.4) (2021-11-23) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.7.0-nightly.1...3.6.4) -**🐛 Bug fixes** - -- Nuke: inventory update removes all loaded read nodes [\#2294](https://github.com/pypeclub/OpenPype/pull/2294) - ## [3.6.3](https://github.com/pypeclub/OpenPype/tree/3.6.3) (2021-11-19) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.6.3-nightly.1...3.6.3) -**🐛 Bug fixes** - -- Deadline: Fix publish targets [\#2280](https://github.com/pypeclub/OpenPype/pull/2280) - ## [3.6.2](https://github.com/pypeclub/OpenPype/tree/3.6.2) (2021-11-18) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.6.2-nightly.2...3.6.2) diff --git a/openpype/version.py b/openpype/version.py index 273755dfd03..bfedfd3d111 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.7.0-nightly.10" +__version__ = "3.7.0-nightly.11" diff --git a/pyproject.toml b/pyproject.toml index ea6d9ee5e56..745a9b3511c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.7.0-nightly.10" # OpenPype +version = "3.7.0-nightly.11" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From b30629866abeb5f5e86ab88dda36bdbf8f2a4cb2 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 28 Dec 2021 14:23:37 +0100 Subject: [PATCH 302/492] Improve lib.polyConstraint performance when Select tool is not the active tool context --- openpype/hosts/maya/api/lib.py | 57 +++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 11 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 52ebcaff649..af9a16b291e 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -745,6 +745,33 @@ def namespaced(namespace, new=True): cmds.namespace(set=original) +@contextlib.contextmanager +def maintained_selection_api(): + """Maintain selection using the Maya Python API. + + Warning: This is *not* added to the undo stack. + + """ + original = om.MGlobal.getActiveSelectionList() + try: + yield + finally: + om.MGlobal.setActiveSelectionList(original) + + +@contextlib.contextmanager +def tool(context): + """Set a tool context during the context manager. + + """ + original = cmds.currentCtx() + try: + cmds.setToolTo(context) + yield + finally: + cmds.setToolTo(original) + + def polyConstraint(components, *args, **kwargs): """Return the list of *components* with the constraints applied. @@ -763,17 +790,25 @@ def polyConstraint(components, *args, **kwargs): kwargs.pop('mode', None) with no_undo(flush=False): - with maya.maintained_selection(): - # Apply constraint using mode=2 (current and next) so - # it applies to the selection made before it; because just - # a `maya.cmds.select()` call will not trigger the constraint. - with reset_polySelectConstraint(): - cmds.select(components, r=1, noExpand=True) - cmds.polySelectConstraint(*args, mode=2, **kwargs) - result = cmds.ls(selection=True) - cmds.select(clear=True) - - return result + # Reverting selection to the original selection using + # `maya.cmds.select` can be slow in rare cases where previously + # `maya.cmds.polySelectConstraint` had set constrain to "All and Next" + # and the "Random" setting was activated. To work around this we + # revert to the original selection using the Maya API. This is safe + # since we're not generating any undo change anyway. + with tool("selectSuperContext"): + # Selection can be very slow when in a manipulator mode. + # So we force the selection context which is fast. + with maintained_selection_api(): + # Apply constraint using mode=2 (current and next) so + # it applies to the selection made before it; because just + # a `maya.cmds.select()` call will not trigger the constraint. + with reset_polySelectConstraint(): + cmds.select(components, r=1, noExpand=True) + return cmds.polySelectConstraint(*args, + mode=2, + returnSelection=True, + **kwargs) @contextlib.contextmanager From 86771ae01dfbdaca373cc65f05066ec86a7a48cb Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 28 Dec 2021 14:34:42 +0100 Subject: [PATCH 303/492] Optimization: Improve speed slightly more (somehow this is faster in most cases) --- openpype/hosts/maya/api/lib.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index af9a16b291e..bd83b13b067 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -805,10 +805,10 @@ def polyConstraint(components, *args, **kwargs): # a `maya.cmds.select()` call will not trigger the constraint. with reset_polySelectConstraint(): cmds.select(components, r=1, noExpand=True) - return cmds.polySelectConstraint(*args, - mode=2, - returnSelection=True, - **kwargs) + cmds.polySelectConstraint(*args, mode=2, **kwargs) + result = cmds.ls(selection=True) + cmds.select(clear=True) + return result @contextlib.contextmanager From 61b7b5eeee74ab17a592678bd3c411ff073e4fe3 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 28 Dec 2021 16:34:19 +0100 Subject: [PATCH 304/492] Fix #2449 - Remove unique name counter --- .../hosts/houdini/plugins/load/load_alembic.py | 13 +++---------- .../hosts/houdini/plugins/load/load_camera.py | 17 ++++------------- 2 files changed, 7 insertions(+), 23 deletions(-) diff --git a/openpype/hosts/houdini/plugins/load/load_alembic.py b/openpype/hosts/houdini/plugins/load/load_alembic.py index cd0f0f0d2d3..df66d56008a 100644 --- a/openpype/hosts/houdini/plugins/load/load_alembic.py +++ b/openpype/hosts/houdini/plugins/load/load_alembic.py @@ -1,6 +1,6 @@ from avalon import api -from avalon.houdini import pipeline, lib +from avalon.houdini import pipeline class AbcLoader(api.Loader): @@ -25,16 +25,9 @@ def load(self, context, name=None, namespace=None, data=None): # Get the root node obj = hou.node("/obj") - # Create a unique name - counter = 1 + # Define node name namespace = namespace if namespace else context["asset"]["name"] - formatted = "{}_{}".format(namespace, name) if namespace else name - node_name = "{0}_{1:03d}".format(formatted, counter) - - children = lib.children_as_string(hou.node("/obj")) - while node_name in children: - counter += 1 - node_name = "{0}_{1:03d}".format(formatted, counter) + node_name = "{}_{}".format(namespace, name) if namespace else name # Create a new geo node container = obj.createNode("geo", node_name=node_name) diff --git a/openpype/hosts/houdini/plugins/load/load_camera.py b/openpype/hosts/houdini/plugins/load/load_camera.py index 83246b7d971..8b98b7c05e4 100644 --- a/openpype/hosts/houdini/plugins/load/load_camera.py +++ b/openpype/hosts/houdini/plugins/load/load_camera.py @@ -1,5 +1,5 @@ from avalon import api -from avalon.houdini import pipeline, lib +from avalon.houdini import pipeline ARCHIVE_EXPRESSION = ('__import__("_alembic_hom_extensions")' @@ -97,18 +97,9 @@ def load(self, context, name=None, namespace=None, data=None): # Get the root node obj = hou.node("/obj") - # Create a unique name - counter = 1 - asset_name = context["asset"]["name"] - - namespace = namespace or asset_name - formatted = "{}_{}".format(namespace, name) if namespace else name - node_name = "{0}_{1:03d}".format(formatted, counter) - - children = lib.children_as_string(hou.node("/obj")) - while node_name in children: - counter += 1 - node_name = "{0}_{1:03d}".format(formatted, counter) + # Define node name + namespace = namespace if namespace else context["asset"]["name"] + node_name = "{}_{}".format(namespace, name) if namespace else name # Create a archive node container = self.create_and_connect(obj, "alembicarchive", node_name) From 1730c9ce960abda8c2f95c7e4a2bd1db35aefb1c Mon Sep 17 00:00:00 2001 From: OpenPype Date: Wed, 29 Dec 2021 03:42:14 +0000 Subject: [PATCH 305/492] [Automated] Bump version --- CHANGELOG.md | 2 +- openpype/version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b7b5ab20fb..ee3bf6421a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## [3.7.0-nightly.11](https://github.com/pypeclub/OpenPype/tree/HEAD) +## [3.7.0-nightly.12](https://github.com/pypeclub/OpenPype/tree/HEAD) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.6.4...HEAD) diff --git a/openpype/version.py b/openpype/version.py index bfedfd3d111..f00f45b5d16 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.7.0-nightly.11" +__version__ = "3.7.0-nightly.12" diff --git a/pyproject.toml b/pyproject.toml index 745a9b3511c..a48a6a44391 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.7.0-nightly.11" # OpenPype +version = "3.7.0-nightly.12" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From bfc6ad0b655869cb00c154b0a2d62d68d8acd20b Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 30 Dec 2021 14:17:56 +0100 Subject: [PATCH 306/492] Fix #2453 Refactor missing _get_reference_node method --- openpype/hosts/maya/plugins/load/load_look.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/load/load_look.py b/openpype/hosts/maya/plugins/load/load_look.py index fca612eff49..8e14778fd2e 100644 --- a/openpype/hosts/maya/plugins/load/load_look.py +++ b/openpype/hosts/maya/plugins/load/load_look.py @@ -8,6 +8,8 @@ from openpype.widgets.message_window import ScrollMessageBox from Qt import QtWidgets +from openpype.hosts.maya.api.plugin import get_reference_node + class LookLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): """Specific loader for lookdev""" @@ -70,7 +72,7 @@ def update(self, container, representation): # Get reference node from container members members = cmds.sets(node, query=True, nodesOnly=True) - reference_node = self._get_reference_node(members) + reference_node = get_reference_node(members, log=self.log) shader_nodes = cmds.ls(members, type='shadingEngine') orig_nodes = set(self._get_nodes_with_shader(shader_nodes)) From 1e49b7c87c4d8adad4c6b468ee9adbb8c391af4a Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 30 Dec 2021 14:45:51 +0100 Subject: [PATCH 307/492] Do not keep fixed geometry vertices selected/active after repair --- .../hosts/maya/plugins/publish/validate_shape_zero.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_shape_zero.py b/openpype/hosts/maya/plugins/publish/validate_shape_zero.py index 2c594ef5f32..acc42f073a5 100644 --- a/openpype/hosts/maya/plugins/publish/validate_shape_zero.py +++ b/openpype/hosts/maya/plugins/publish/validate_shape_zero.py @@ -4,6 +4,8 @@ import openpype.api import openpype.hosts.maya.api.action +from avalon.maya import maintained_selection + class ValidateShapeZero(pyblish.api.Validator): """shape can't have any values @@ -47,8 +49,12 @@ def get_invalid(instance): @classmethod def repair(cls, instance): invalid_shapes = cls.get_invalid(instance) - for shape in invalid_shapes: - cmds.polyCollapseTweaks(shape) + if not invalid_shapes: + return + + with maintained_selection(): + for shape in invalid_shapes: + cmds.polyCollapseTweaks(shape) def process(self, instance): """Process all the nodes in the instance "objectSet""" From 1d94607037bad9393fb9ed82fae2842bbe66a626 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 30 Dec 2021 16:57:42 +0100 Subject: [PATCH 308/492] Optimize validation speed for dense polymeshes (especially those that have locked normal) --- .../publish/validate_mesh_normals_unlocked.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_mesh_normals_unlocked.py b/openpype/hosts/maya/plugins/publish/validate_mesh_normals_unlocked.py index b14781b6082..750932df54c 100644 --- a/openpype/hosts/maya/plugins/publish/validate_mesh_normals_unlocked.py +++ b/openpype/hosts/maya/plugins/publish/validate_mesh_normals_unlocked.py @@ -1,4 +1,5 @@ from maya import cmds +import maya.api.OpenMaya as om2 import pyblish.api import openpype.api @@ -25,10 +26,16 @@ class ValidateMeshNormalsUnlocked(pyblish.api.Validator): @staticmethod def has_locked_normals(mesh): - """Return whether a mesh node has locked normals""" - return any(cmds.polyNormalPerVertex("{}.vtxFace[*][*]".format(mesh), - query=True, - freezeNormal=True)) + """Return whether mesh has at least one locked normal""" + + sel = om2.MGlobal.getSelectionListByName(mesh) + node = sel.getDependNode(0) + fn_mesh = om2.MFnMesh(node) + _, normal_ids = fn_mesh.getNormalIds() + for normal_id in normal_ids: + if fn_mesh.isNormalLocked(normal_id): + return True + return False @classmethod def get_invalid(cls, instance): From 73400612435880f731230a18afa603b0ad05df3b Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 30 Dec 2021 17:31:07 +0100 Subject: [PATCH 309/492] Fix repair taking very long time for many heavy meshes (optimization) --- .../hosts/maya/plugins/publish/validate_shape_zero.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_shape_zero.py b/openpype/hosts/maya/plugins/publish/validate_shape_zero.py index acc42f073a5..bb601b8f50c 100644 --- a/openpype/hosts/maya/plugins/publish/validate_shape_zero.py +++ b/openpype/hosts/maya/plugins/publish/validate_shape_zero.py @@ -3,6 +3,7 @@ import pyblish.api import openpype.api import openpype.hosts.maya.api.action +from openpype.hosts.maya.api import lib from avalon.maya import maintained_selection @@ -53,8 +54,13 @@ def repair(cls, instance): return with maintained_selection(): - for shape in invalid_shapes: - cmds.polyCollapseTweaks(shape) + with lib.tool("selectSuperContext"): + for shape in invalid_shapes: + cmds.polyCollapseTweaks(shape) + # cmds.polyCollapseTweaks keeps selecting the geometry + # after each command. When running on many meshes + # after one another this tends to get really heavy + cmds.select(clear=True) def process(self, instance): """Process all the nodes in the instance "objectSet""" From c4d91fb9c0e2fb6a57600c9dc18b319903735994 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 30 Dec 2021 17:34:18 +0100 Subject: [PATCH 310/492] Improve docstring and error message - Previously it said something about translate, rotate and scale. However this validator doesn't check that at all and thus the docstring was incorrect. --- .../hosts/maya/plugins/publish/validate_shape_zero.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_shape_zero.py b/openpype/hosts/maya/plugins/publish/validate_shape_zero.py index bb601b8f50c..6b5c5d1398f 100644 --- a/openpype/hosts/maya/plugins/publish/validate_shape_zero.py +++ b/openpype/hosts/maya/plugins/publish/validate_shape_zero.py @@ -9,11 +9,9 @@ class ValidateShapeZero(pyblish.api.Validator): - """shape can't have any values + """Shape components may not have any "tweak" values - To solve this issue, try freezing the shapes. So long - as the translation, rotation and scaling values are zero, - you're all good. + To solve this issue, try freezing the shapes. """ @@ -67,5 +65,5 @@ def process(self, instance): invalid = self.get_invalid(instance) if invalid: - raise ValueError("Nodes found with shape or vertices not freezed" - "values: {0}".format(invalid)) + raise ValueError("Shapes found with non-zero component tweaks: " + "{0}".format(invalid)) From 3a95b99e42bf3903bbd5e66e6cb2558a5e880353 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 30 Dec 2021 17:56:50 +0100 Subject: [PATCH 311/492] Re-use polyConstraint from openpype.host.maya.api.lib --- .../plugins/publish/validate_mesh_ngons.py | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_mesh_ngons.py b/openpype/hosts/maya/plugins/publish/validate_mesh_ngons.py index e8cc019b527..839aab0d0b1 100644 --- a/openpype/hosts/maya/plugins/publish/validate_mesh_ngons.py +++ b/openpype/hosts/maya/plugins/publish/validate_mesh_ngons.py @@ -7,21 +7,6 @@ from openpype.hosts.maya.api import lib -def polyConstraint(objects, *args, **kwargs): - kwargs.pop('mode', None) - - with lib.no_undo(flush=False): - with maya.maintained_selection(): - with lib.reset_polySelectConstraint(): - cmds.select(objects, r=1, noExpand=True) - # Acting as 'polyCleanupArgList' for n-sided polygon selection - cmds.polySelectConstraint(*args, mode=3, **kwargs) - result = cmds.ls(selection=True) - cmds.select(clear=True) - - return result - - class ValidateMeshNgons(pyblish.api.Validator): """Ensure that meshes don't have ngons @@ -41,8 +26,17 @@ class ValidateMeshNgons(pyblish.api.Validator): @staticmethod def get_invalid(instance): - meshes = cmds.ls(instance, type='mesh') - return polyConstraint(meshes, type=8, size=3) + meshes = cmds.ls(instance, type='mesh', long=True) + + # Get all faces + faces = ['{0}.f[*]'.format(node) for node in meshes] + + # Filter to n-sided polygon faces (ngons) + invalid = lib.polyConstraint(faces, + t=0x0008, # type=face + size=3) # size=nsided + + return invalid def process(self, instance): """Process all the nodes in the instance "objectSet""" From c8da4709fe0b5914aa69011db7f60dae2edd763c Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 30 Dec 2021 21:43:47 +0100 Subject: [PATCH 312/492] Fix errors being logged to script editor when attribute is not user-defined attribute. --- .../hosts/maya/plugins/publish/validate_rig_controllers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_rig_controllers.py b/openpype/hosts/maya/plugins/publish/validate_rig_controllers.py index 4e028d1d24f..d5a1fd35294 100644 --- a/openpype/hosts/maya/plugins/publish/validate_rig_controllers.py +++ b/openpype/hosts/maya/plugins/publish/validate_rig_controllers.py @@ -164,7 +164,8 @@ def get_connected_attributes(control): continue # Ignore proxy connections. - if cmds.addAttr(plug, query=True, usedAsProxy=True): + if (cmds.addAttr(plug, query=True, exists=True) and + cmds.addAttr(plug, query=True, usedAsProxy=True)): continue # Check for incoming connections From fdd7efb2bf7d070dee1c2bea1719b500b3232b57 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 31 Dec 2021 01:19:21 +0100 Subject: [PATCH 313/492] Improve speed of Collect History logic - maya.cmds.listHistory returns duplicates if multiple nodes have the same node in histor, thus making them unique reduces tons of items continuing through all the commands - cmds.ls(type="renderLayer") now without input nodes. History tends to be so big that calling the command with those nodes as arguments produces a big overhead, simultaneously amount of renderlayers usually tend to be relatively small. So we just get all renderlayers and filter ourselves. - use `fastIteration` flag in Maya 2020+ --- .../maya/plugins/publish/collect_history.py | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/collect_history.py b/openpype/hosts/maya/plugins/publish/collect_history.py index 16c8e4342e9..71f0169971f 100644 --- a/openpype/hosts/maya/plugins/publish/collect_history.py +++ b/openpype/hosts/maya/plugins/publish/collect_history.py @@ -22,15 +22,22 @@ class CollectMayaHistory(pyblish.api.InstancePlugin): def process(self, instance): + kwargs = {} + if int(cmds.about(version=True)) >= 2020: + # New flag since Maya 2020 which makes cmds.listHistory faster + kwargs = {"fastIteration": True} + else: + self.log.debug("Ignoring `fastIteration` flag before Maya 2020..") + # Collect the history with long names - history = cmds.listHistory(instance, leaf=False) or [] - history = cmds.ls(history, long=True) - - # Remove invalid node types (like renderlayers) - invalid = cmds.ls(history, type="renderLayer", long=True) - if invalid: - invalid = set(invalid) # optimize lookup - history = [x for x in history if x not in invalid] + history = set(cmds.listHistory(instance, leaf=False, **kwargs) or []) + history = cmds.ls(list(history), long=True) + + # Exclude invalid nodes (like renderlayers) + exclude = cmds.ls(type="renderLayer", long=True) + if exclude: + exclude = set(exclude) # optimize lookup + history = [x for x in history if x not in exclude] # Combine members with history members = instance[:] + history From 0761613e09dce0bf5e028f7dfb8a3b74c0fde913 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 31 Dec 2021 10:39:09 +0100 Subject: [PATCH 314/492] removed usage of sw_folders --- .../action_create_folders.py | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/openpype/modules/default_modules/ftrack/event_handlers_user/action_create_folders.py b/openpype/modules/default_modules/ftrack/event_handlers_user/action_create_folders.py index 994dbd90e48..8bbef9ad737 100644 --- a/openpype/modules/default_modules/ftrack/event_handlers_user/action_create_folders.py +++ b/openpype/modules/default_modules/ftrack/event_handlers_user/action_create_folders.py @@ -111,13 +111,6 @@ def launch(self, session, entities, event): publish_template = publish_template[key] publish_has_apps = "{app" in publish_template - tools_settings = project_settings["global"]["tools"] - app_presets = tools_settings["Workfiles"]["sw_folders"] - app_manager_apps = None - if app_presets and (work_has_apps or publish_has_apps): - app_manager_apps = ApplicationManager().applications - - cached_apps = {} collected_paths = [] for entity in all_entities: if entity.entity_type.lower() == "project": @@ -143,26 +136,10 @@ def launch(self, session, entities, event): if child["object_type"]["name"].lower() != "task": continue tasks_created = True - task_type_name = child["type"]["name"].lower() task_data = ent_data.copy() task_data["task"] = child["name"] apps = [] - if app_manager_apps: - possible_apps = app_presets.get(task_type_name) or [] - for app_name in possible_apps: - - if app_name in cached_apps: - apps.append(cached_apps[app_name]) - continue - - app_def = app_manager_apps.get(app_name) - if app_def and app_def.is_host: - app_dir = app_def.host_name - else: - app_dir = app_name - cached_apps[app_name] = app_dir - apps.append(app_dir) # Template wok if work_has_apps: From fca31274d0b83f31c44301ff00d1ff80017f1ae9 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 31 Dec 2021 11:00:24 +0100 Subject: [PATCH 315/492] replaced sw_folders with extra_folders --- .../defaults/project_settings/global.json | 16 +------- .../schemas/schema_global_tools.json | 40 ++++++++++++++++--- 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/openpype/settings/defaults/project_settings/global.json b/openpype/settings/defaults/project_settings/global.json index 55732f80ce0..cff1259c98f 100644 --- a/openpype/settings/defaults/project_settings/global.json +++ b/openpype/settings/defaults/project_settings/global.json @@ -291,21 +291,7 @@ "enabled": false } ], - "sw_folders": { - "compositing": [ - "nuke", - "ae" - ], - "modeling": [ - "maya", - "blender", - "zbrush" - ], - "lookdev": [ - "substance", - "textures" - ] - } + "extra_folders": [] }, "loader": { "family_filter_profiles": [ diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_tools.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_tools.json index 26d3771d8a5..10f15e41048 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_tools.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_tools.json @@ -195,14 +195,44 @@ } }, { - "type": "dict-modifiable", + "type": "list", + "key": "extra_folders", + "label": "Extra work folders", "collapsible": true, - "key": "sw_folders", - "label": "Extra task folders", + "use_label_wrap": true, "is_group": true, "object_type": { - "type": "list", - "object_type": "text" + "type": "dict", + "children": [ + { + "type": "hosts-enum", + "key": "hosts", + "label": "Hosts", + "multiselection": true + }, + { + "type": "task-types-enum", + "key": "task_types", + "label": "Task types" + }, + { + "label": "Task names", + "key": "task_names", + "type": "list", + "object_type": "text" + }, + { + "type": "splitter" + }, + { + "key": "folders", + "label": "Folders", + "type": "list", + "highlight_content": true, + "collapsible": false, + "object_type": "text" + } + ] } } ] From 8785536ad38af9101928395f70947e764680267e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 31 Dec 2021 12:41:43 +0100 Subject: [PATCH 316/492] modified imports --- openpype/lib/path_tools.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/openpype/lib/path_tools.py b/openpype/lib/path_tools.py index 9bb0231ca74..ea6258f9f9b 100644 --- a/openpype/lib/path_tools.py +++ b/openpype/lib/path_tools.py @@ -1,13 +1,14 @@ -import json -import logging import os import re import abc +import json +import logging import six +from openpype.settings import get_project_settings +from openpype.settings.lib import get_site_local_overrides from .anatomy import Anatomy -from openpype.settings import get_project_settings log = logging.getLogger(__name__) From 22a2a433c15477d7ff625097c3374002c1233d04 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 31 Dec 2021 12:42:01 +0100 Subject: [PATCH 317/492] created function to create extra folders in workdir --- openpype/lib/__init__.py | 4 ++- openpype/lib/path_tools.py | 53 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/openpype/lib/__init__.py b/openpype/lib/__init__.py index c99e3bc28d7..0b8a4dfa154 100644 --- a/openpype/lib/__init__.py +++ b/openpype/lib/__init__.py @@ -148,7 +148,8 @@ get_version_from_path, get_last_version_from_path, create_project_folders, - get_project_basic_paths + create_workdir_extra_folders, + get_project_basic_paths, ) from .editorial import ( @@ -290,6 +291,7 @@ "frames_to_timecode", "make_sequence_collection", "create_project_folders", + "create_workdir_extra_folders", "get_project_basic_paths", "get_openpype_version", diff --git a/openpype/lib/path_tools.py b/openpype/lib/path_tools.py index ea6258f9f9b..12e9e2db9c6 100644 --- a/openpype/lib/path_tools.py +++ b/openpype/lib/path_tools.py @@ -9,6 +9,7 @@ from openpype.settings.lib import get_site_local_overrides from .anatomy import Anatomy +from .profiles_filtering import filter_profiles log = logging.getLogger(__name__) @@ -201,6 +202,58 @@ def get_project_basic_paths(project_name): return _list_path_items(folder_structure) +def create_workdir_extra_folders( + workdir, host_name, task_type, task_name, project_name, + project_settings=None +): + """Create extra folders in work directory based on context. + + Args: + workdir (str): Path to workdir where workfiles is stored. + host_name (str): Name of host implementation. + task_type (str): Type of task for which extra folders should be + created. + task_name (str): Name of task for which extra folders should be + created. + project_name (str): Name of project on which task is. + project_settings (dict): Prepared project settings. Are loaded if not + passed. + """ + # Load project settings if not set + if not project_settings: + project_settings = get_project_settings(project_name) + + # Load extra folders profiles + extra_folders_profiles = ( + project_settings["global"]["tools"]["Workfiles"]["extra_folders"] + ) + # Skip if are empty + if not extra_folders_profiles: + return + + # Prepare profiles filters + filter_data = { + "task_types": task_type, + "task_names": task_name, + "hosts": host_name + } + profile = filter_profiles(extra_folders_profiles, filter_data) + if profile is None: + return + + for subfolder in profile["folders"]: + # Make sure backslashes are converted to forwards slashes + # and does not start with slash + subfolder = subfolder.replace("\\", "/").lstrip("/") + # Skip empty strings + if not subfolder: + continue + + fullpath = os.path.join(workdir, subfolder) + if not os.path.exists(fullpath): + os.makedirs(fullpath) + + @six.add_metaclass(abc.ABCMeta) class HostDirmap: """ From ecbe66a0d2a65b777d4352577db8f7dbe32b7b76 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 31 Dec 2021 12:42:36 +0100 Subject: [PATCH 318/492] use trigger create workdir extra folders in workfiles tool --- openpype/tools/workfiles/app.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/openpype/tools/workfiles/app.py b/openpype/tools/workfiles/app.py index 7973b88b828..80e0957feeb 100644 --- a/openpype/tools/workfiles/app.py +++ b/openpype/tools/workfiles/app.py @@ -25,7 +25,8 @@ get_workfile_doc, create_workfile_doc, save_workfile_data_to_doc, - get_workfile_template_key + get_workfile_template_key, + create_workdir_extra_folders ) from .model import FilesModel @@ -672,7 +673,13 @@ def on_save_as_pressed(self): self.set_asset_task( self._asset_id, self._task_name, self._task_type ) - + create_workdir_extra_folders( + self.root, + api.Session["AVALON_APP"], + self._task_type, + self._task_name, + api.Session["AVALON_PROJECT"] + ) pipeline.emit("after.workfile.save", [file_path]) self.workfile_created.emit(file_path) From b86a4ea95b5356f368919d037f3922f331595753 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 31 Dec 2021 12:46:01 +0100 Subject: [PATCH 319/492] don't use schedule in workfiles tool --- openpype/tools/workfiles/app.py | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/openpype/tools/workfiles/app.py b/openpype/tools/workfiles/app.py index 80e0957feeb..0615ec0aca7 100644 --- a/openpype/tools/workfiles/app.py +++ b/openpype/tools/workfiles/app.py @@ -12,7 +12,6 @@ from openpype import style from openpype.tools.utils.lib import ( - schedule, qt_app_context ) from openpype.tools.utils import PlaceholderLineEdit @@ -736,7 +735,7 @@ def refresh(self): self.files_model.refresh() if self.auto_select_latest_modified: - schedule(self._select_last_modified_file, 100) + self._select_last_modified_file() def on_context_menu(self, point): index = self.files_view.indexAt(point) @@ -941,8 +940,8 @@ def __init__(self, parent=None): # Connect signals set_context_timer.timeout.connect(self._on_context_set_timeout) - assets_widget.selection_changed.connect(self.on_asset_changed) - tasks_widget.task_changed.connect(self.on_task_changed) + assets_widget.selection_changed.connect(self._on_asset_changed) + tasks_widget.task_changed.connect(self._on_task_changed) files_widget.file_selected.connect(self.on_file_select) files_widget.workfile_created.connect(self.on_workfile_create) files_widget.file_opened.connect(self._on_file_opened) @@ -987,13 +986,6 @@ def keyPressEvent(self, event): def set_save_enabled(self, enabled): self.files_widget.btn_save.setEnabled(enabled) - def on_task_changed(self): - # Since we query the disk give it slightly more delay - schedule(self._on_task_changed, 100, channel="mongo") - - def on_asset_changed(self): - schedule(self._on_asset_changed, 50, channel="mongo") - def on_file_select(self, filepath): asset_id = self.assets_widget.get_selected_asset_id() task_name = self.tasks_widget.get_selected_task_name() From d68992a76494706925ffac4b8180544459b5822a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 31 Dec 2021 12:56:52 +0100 Subject: [PATCH 320/492] added prelaunch hook that will create extra folders in workdir for launch context --- .../hooks/pre_create_extra_workdir_folders.py | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 openpype/hooks/pre_create_extra_workdir_folders.py diff --git a/openpype/hooks/pre_create_extra_workdir_folders.py b/openpype/hooks/pre_create_extra_workdir_folders.py new file mode 100644 index 00000000000..d79c5831ee1 --- /dev/null +++ b/openpype/hooks/pre_create_extra_workdir_folders.py @@ -0,0 +1,33 @@ +import os +from openpype.lib import ( + PreLaunchHook, + create_workdir_extra_folders +) + + +class AddLastWorkfileToLaunchArgs(PreLaunchHook): + """Add last workfile path to launch arguments. + + This is not possible to do for all applications the same way. + """ + + # Execute after workfile template copy + order = 15 + + def execute(self): + if not self.application.is_host: + return + + env = self.data.get("env") or {} + workdir = env.get("AVALON_WORKDIR") + if not workdir or not os.path.exists(workdir): + return + + host_name = self.application.host_name + task_type = self.data["task_type"] + task_name = self.data["task_name"] + project_name = self.data["project_name"] + + create_workdir_extra_folders( + workdir, host_name, task_type, task_name, project_name, + ) From 8826b6dfa2519feb5a28ea4264a8c8e51db74ce0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 31 Dec 2021 13:02:29 +0100 Subject: [PATCH 321/492] added label --- .../schemas/projects_schema/schemas/schema_global_tools.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_tools.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_tools.json index 10f15e41048..bb71c9bde65 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_tools.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_tools.json @@ -224,6 +224,10 @@ { "type": "splitter" }, + { + "type": "label", + "label": "Folders will be created in directory next to workfile. Items may contain nested directories (e.g. resources/images)." + }, { "key": "folders", "label": "Folders", From 2cbe986f9fbdead632b7adf85e7545d09ff3cebc Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 31 Dec 2021 14:09:25 +0100 Subject: [PATCH 322/492] Fix error when clicking on empty Subset area in Loader after having a Subset selected. - Also reorder the code so variables are closer to where they are used to avoid confusion --- openpype/tools/loader/app.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/openpype/tools/loader/app.py b/openpype/tools/loader/app.py index 583065633b1..62bf5538de6 100644 --- a/openpype/tools/loader/app.py +++ b/openpype/tools/loader/app.py @@ -363,7 +363,6 @@ def _versionschanged(self): # Active must be in the selected rows otherwise we # assume it's not actually an "active" current index. - version_docs = None version_doc = None active = selection.currentIndex() rows = selection.selectedRows(column=active.column()) @@ -375,9 +374,10 @@ def _versionschanged(self): not (item.get("isGroup") or item.get("isMerged")) ): version_doc = item["version_document"] + self._version_info_widget.set_version(version_doc) + version_docs = [] if rows: - version_docs = [] for index in rows: if not index or not index.isValid(): continue @@ -390,8 +390,6 @@ def _versionschanged(self): else: version_docs.append(item["version_document"]) - self._version_info_widget.set_version(version_doc) - thumbnail_src_ids = [ version_doc["_id"] for version_doc in version_docs @@ -402,7 +400,7 @@ def _versionschanged(self): self._thumbnail_widget.set_thumbnail(thumbnail_src_ids) if self._repres_widget is not None: - version_ids = [doc["_id"] for doc in version_docs or []] + version_ids = [doc["_id"] for doc in version_docs] self._repres_widget.set_version_ids(version_ids) # self._repres_widget.change_visibility("subset", len(rows) > 1) From 6f2a9bc116e624c3ed259d972e13c3356b5b392e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 31 Dec 2021 14:31:39 +0100 Subject: [PATCH 323/492] copied main thread processor from new publisher --- openpype/tools/pyblish_pype/control.py | 66 ++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/openpype/tools/pyblish_pype/control.py b/openpype/tools/pyblish_pype/control.py index 2a9e67097ec..331114fba49 100644 --- a/openpype/tools/pyblish_pype/control.py +++ b/openpype/tools/pyblish_pype/control.py @@ -28,6 +28,72 @@ class IterationBreak(Exception): pass +class MainThreadItem: + """Callback with args and kwargs.""" + def __init__(self, callback, *args, **kwargs): + self.callback = callback + self.args = args + self.kwargs = kwargs + + def process(self): + self.callback(*self.args, **self.kwargs) + + +class MainThreadProcess(QtCore.QObject): + """Qt based main thread process executor. + + Has timer which controls each 50ms if there is new item to process. + + This approach gives ability to update UI meanwhile plugin is in progress. + """ + def __init__(self): + super(MainThreadProcess, self).__init__() + self._items_to_process = collections.deque() + + timer = QtCore.QTimer() + timer.setInterval(10) + + timer.timeout.connect(self._execute) + + self._timer = timer + + def process(self, func, *args, **kwargs): + item = MainThreadItem(func, *args, **kwargs) + self.add_item(item) + + def add_item(self, item): + self._items_to_process.append(item) + + def _execute(self): + if not self._items_to_process: + return + + item = self._items_to_process.popleft() + item.process() + + def start(self): + if not self._timer.isActive(): + self._timer.start() + + def stop(self): + if self._timer.isActive(): + self._timer.stop() + + def clear(self): + if self._timer.isActive(): + self._timer.stop() + self._items_to_process = collections.deque() + + def stop_if_empty(self): + if self._timer.isActive(): + item = MainThreadItem(self._stop_if_empty) + self.add_item(item) + + def _stop_if_empty(self): + if not self._items_to_process: + self.stop() + + class Controller(QtCore.QObject): log = logging.getLogger("PyblishController") # Emitted when the GUI is about to start processing; From 3029b7b148318db9462abf44afbc691f9f888802 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 31 Dec 2021 14:32:00 +0100 Subject: [PATCH 324/492] do not call reset, collect, validate and publish in deffer --- openpype/tools/pyblish_pype/window.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/openpype/tools/pyblish_pype/window.py b/openpype/tools/pyblish_pype/window.py index 536f7932166..fdd2d80e23c 100644 --- a/openpype/tools/pyblish_pype/window.py +++ b/openpype/tools/pyblish_pype/window.py @@ -1148,7 +1148,7 @@ def reset(self): self.comment_box.placeholder.setVisible(False) self.comment_box.placeholder.setVisible(True) # Launch controller reset - util.defer(500, self.controller.reset) + self.controller.reset() def validate(self): self.info(self.tr("Preparing validate..")) @@ -1159,7 +1159,7 @@ def validate(self): self.button_suspend_logs.setEnabled(False) - util.defer(5, self.controller.validate) + self.controller.validate() def publish(self): self.info(self.tr("Preparing publish..")) @@ -1170,7 +1170,7 @@ def publish(self): self.button_suspend_logs.setEnabled(False) - util.defer(5, self.controller.publish) + self.controller.publish() def act(self, plugin_item, action): self.info("%s %s.." % (self.tr("Preparing"), action)) @@ -1187,9 +1187,7 @@ def act(self, plugin_item, action): ) # Give Qt time to draw - util.defer(100, lambda: self.controller.act( - plugin_item.plugin, action - )) + self.controller.act(plugin_item.plugin, action) self.info(self.tr("Action prepared.")) @@ -1267,7 +1265,7 @@ def on_problem(): self.info(self.tr("..as soon as processing is finished..")) self.controller.stop() self.finished.connect(self.close) - util.defer(2000, on_problem) + util.defer(200, on_problem) return event.ignore() self.state["is_closing"] = True From 8cc389a63f3c9dfedcad72256b3187ae767393e2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 31 Dec 2021 14:32:34 +0100 Subject: [PATCH 325/492] modified processing to not use defer function --- openpype/tools/pyblish_pype/control.py | 84 +++++++++++++++++++------- 1 file changed, 63 insertions(+), 21 deletions(-) diff --git a/openpype/tools/pyblish_pype/control.py b/openpype/tools/pyblish_pype/control.py index 331114fba49..65faa185d25 100644 --- a/openpype/tools/pyblish_pype/control.py +++ b/openpype/tools/pyblish_pype/control.py @@ -9,6 +9,7 @@ import sys import inspect import logging +import collections from Qt import QtCore @@ -137,6 +138,7 @@ def __init__(self, parent=None): self.plugins = {} self.optional_default = {} self.instance_toggled.connect(self._on_instance_toggled) + self._main_thread_processor = MainThreadProcess() def reset_variables(self): self.log.debug("Resetting pyblish context variables") @@ -235,7 +237,11 @@ def reset_context(self): def reset(self): """Discover plug-ins and run collection.""" + self._main_thread_processor.clear() + self._main_thread_processor.process(self._reset) + self._main_thread_processor.start() + def _reset(self): self.reset_context() self.reset_variables() @@ -276,21 +282,25 @@ def on_published(self): if self.is_running: self.is_running = False self.was_finished.emit() + self._main_thread_processor.stop() def stop(self): self.log.debug("Stopping") self.stopped = True def act(self, plugin, action): - def on_next(): - result = pyblish.plugin.process( - plugin, self.context, None, action.id - ) - self.is_running = False - self.was_acted.emit(result) - self.is_running = True - util.defer(100, on_next) + item = MainThreadItem(self._process_action, plugin, action) + self._main_thread_processor.add_item(item) + self._main_thread_processor.start() + self._main_thread_processor.stop_if_empty() + + def _process_action(self, plugin, action): + result = pyblish.plugin.process( + plugin, self.context, None, action.id + ) + self.is_running = False + self.was_acted.emit(result) def emit_(self, signal, kwargs): pyblish.api.emit(signal, **kwargs) @@ -421,11 +431,13 @@ def _pair_yielder(self, plugins): self.passed_group.emit(self.processing["next_group_order"]) - def iterate_and_process(self, on_finished=lambda: None): + def iterate_and_process(self, on_finished=None): """ Iterating inserted plugins with current context. Collectors do not contain instances, they are None when collecting! This process don't stop on one """ + self._main_thread_processor.start() + def on_next(): self.log.debug("Looking for next pair to process") try: @@ -437,13 +449,19 @@ def on_next(): self.log.debug("Iteration break was raised") self.is_running = False self.was_stopped.emit() + self._main_thread_processor.stop() return except StopIteration: self.log.debug("Iteration stop was raised") self.is_running = False # All pairs were processed successfully! - return util.defer(500, on_finished) + if on_finished is not None: + self._main_thread_processor.add_item( + MainThreadItem(on_finished) + ) + self._main_thread_processor.stop_if_empty() + return except Exception as exc: self.log.warning( @@ -451,12 +469,15 @@ def on_next(): exc_info=True ) exc_msg = str(exc) - return util.defer( - 500, lambda: on_unexpected_error(error=exc_msg) + self._main_thread_processor.add_item( + MainThreadItem(on_unexpected_error, error=exc_msg) ) + return self.about_to_process.emit(*self.current_pair) - util.defer(100, on_process) + self._main_thread_processor.add_item( + MainThreadItem(on_process) + ) def on_process(): try: @@ -477,11 +498,14 @@ def on_process(): exc_info=True ) exc_msg = str(exc) - return util.defer( - 500, lambda: on_unexpected_error(error=exc_msg) + self._main_thread_processor.add_item( + MainThreadItem(on_unexpected_error, error=exc_msg) ) + return - util.defer(10, on_next) + self._main_thread_processor.add_item( + MainThreadItem(on_next) + ) def on_unexpected_error(error): # TODO this should be handled much differently @@ -489,24 +513,42 @@ def on_unexpected_error(error): self.is_running = False self.was_stopped.emit() util.u_print(u"An unexpected error occurred:\n %s" % error) - return util.defer(500, on_finished) + if on_finished is not None: + self._main_thread_processor.add_item( + MainThreadItem(on_finished) + ) + self._main_thread_processor.stop_if_empty() self.is_running = True - util.defer(10, on_next) + self._main_thread_processor.add_item( + MainThreadItem(on_next) + ) def collect(self): """ Iterate and process Collect plugins - load_plugins method is launched again when finished """ - self.iterate_and_process() + self._main_thread_processor.process(self._start_collect) + self._main_thread_processor.start() def validate(self): """ Process plugins to validations_order value.""" - self.processing["stop_on_validation"] = True - self.iterate_and_process() + self._main_thread_processor.process(self._start_validate) + self._main_thread_processor.start() def publish(self): """ Iterate and process all remaining plugins.""" + self._main_thread_processor.process(self._start_publish) + self._main_thread_processor.start() + + def _start_collect(self): + self.iterate_and_process() + + def _start_validate(self): + self.processing["stop_on_validation"] = True + self.iterate_and_process() + + def _start_publish(self): self.processing["stop_on_validation"] = False self.iterate_and_process(self.on_published) From 0289bb58bef7530d203c20ebef0e17afb272ae90 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 31 Dec 2021 14:33:26 +0100 Subject: [PATCH 326/492] modified timer interval in new publisher --- openpype/tools/publisher/control.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/tools/publisher/control.py b/openpype/tools/publisher/control.py index 24ec9dcb0ed..dd971449777 100644 --- a/openpype/tools/publisher/control.py +++ b/openpype/tools/publisher/control.py @@ -41,12 +41,15 @@ class MainThreadProcess(QtCore.QObject): This approach gives ability to update UI meanwhile plugin is in progress. """ + + timer_interval = 10 + def __init__(self): super(MainThreadProcess, self).__init__() self._items_to_process = collections.deque() timer = QtCore.QTimer() - timer.setInterval(50) + timer.setInterval(self.timer_interval) timer.timeout.connect(self._execute) From 1d087b6232588ff7659b9979ee0773259f2ebc54 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 31 Dec 2021 16:25:56 +0100 Subject: [PATCH 327/492] lowered timer interval to 3 ms --- openpype/tools/publisher/control.py | 2 +- openpype/tools/pyblish_pype/control.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/tools/publisher/control.py b/openpype/tools/publisher/control.py index dd971449777..860c009f152 100644 --- a/openpype/tools/publisher/control.py +++ b/openpype/tools/publisher/control.py @@ -42,7 +42,7 @@ class MainThreadProcess(QtCore.QObject): This approach gives ability to update UI meanwhile plugin is in progress. """ - timer_interval = 10 + timer_interval = 3 def __init__(self): super(MainThreadProcess, self).__init__() diff --git a/openpype/tools/pyblish_pype/control.py b/openpype/tools/pyblish_pype/control.py index 65faa185d25..d2b74e316af 100644 --- a/openpype/tools/pyblish_pype/control.py +++ b/openpype/tools/pyblish_pype/control.py @@ -47,12 +47,14 @@ class MainThreadProcess(QtCore.QObject): This approach gives ability to update UI meanwhile plugin is in progress. """ + timer_interval = 3 + def __init__(self): super(MainThreadProcess, self).__init__() self._items_to_process = collections.deque() timer = QtCore.QTimer() - timer.setInterval(10) + timer.setInterval(self.timer_interval) timer.timeout.connect(self._execute) From f3d9c981b6f817aa0538b9f9618a68adae470a2f Mon Sep 17 00:00:00 2001 From: OpenPype Date: Sat, 1 Jan 2022 03:43:34 +0000 Subject: [PATCH 328/492] [Automated] Bump version --- CHANGELOG.md | 2 +- openpype/version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee3bf6421a5..ef6c04c06fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## [3.7.0-nightly.12](https://github.com/pypeclub/OpenPype/tree/HEAD) +## [3.7.0-nightly.13](https://github.com/pypeclub/OpenPype/tree/HEAD) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.6.4...HEAD) diff --git a/openpype/version.py b/openpype/version.py index f00f45b5d16..891cd4fd52b 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.7.0-nightly.12" +__version__ = "3.7.0-nightly.13" diff --git a/pyproject.toml b/pyproject.toml index a48a6a44391..adf3e3a25da 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.7.0-nightly.12" # OpenPype +version = "3.7.0-nightly.13" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From 676e68f56b2bef3dcc479cdb69175d552da15116 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 3 Jan 2022 12:41:34 +0100 Subject: [PATCH 329/492] make sure anatomy does not return unicode strings --- openpype/lib/anatomy.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/lib/anatomy.py b/openpype/lib/anatomy.py index 66ecbd66d15..5f7285fe6c6 100644 --- a/openpype/lib/anatomy.py +++ b/openpype/lib/anatomy.py @@ -1568,8 +1568,11 @@ def _root_environments(self, keys=None, roots=None): key_items = [self.env_prefix] for _key in keys: key_items.append(_key.upper()) + key = "_".join(key_items) - return {key: roots.value} + # Make sure key and value does not contain unicode + # - can happen in Python 2 hosts + return {str(key): str(roots.value)} output = {} for _key, _value in roots.items(): From 73beba9181dd8e71b90169e056d875b5661c36b1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 3 Jan 2022 12:51:37 +0100 Subject: [PATCH 330/492] fix last import from avalon --- openpype/hosts/tvpaint/plugins/publish/extract_sequence.py | 2 +- openpype/hosts/tvpaint/worker/worker.py | 2 +- openpype/hosts/tvpaint/worker/worker_job.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/publish/extract_sequence.py b/openpype/hosts/tvpaint/plugins/publish/extract_sequence.py index 31f685919ef..b6b8bd0d9ef 100644 --- a/openpype/hosts/tvpaint/plugins/publish/extract_sequence.py +++ b/openpype/hosts/tvpaint/plugins/publish/extract_sequence.py @@ -5,7 +5,7 @@ from PIL import Image import pyblish.api -from avalon.tvpaint import lib +from openpype.hosts.tvpaint.api import lib from openpype.hosts.tvpaint.lib import ( calculate_layers_extraction_data, get_frame_filename_template, diff --git a/openpype/hosts/tvpaint/worker/worker.py b/openpype/hosts/tvpaint/worker/worker.py index 738656fa91a..cfd40bc7ba6 100644 --- a/openpype/hosts/tvpaint/worker/worker.py +++ b/openpype/hosts/tvpaint/worker/worker.py @@ -2,7 +2,7 @@ import time import asyncio -from avalon.tvpaint.communication_server import ( +from openpype.hosts.tvpaint.api.communication_server import ( BaseCommunicator, CommunicationWrapper ) diff --git a/openpype/hosts/tvpaint/worker/worker_job.py b/openpype/hosts/tvpaint/worker/worker_job.py index c3893b6f2ea..519d42ce73e 100644 --- a/openpype/hosts/tvpaint/worker/worker_job.py +++ b/openpype/hosts/tvpaint/worker/worker_job.py @@ -256,7 +256,7 @@ class CollectSceneData(BaseCommand): name = "collect_scene_data" def execute(self): - from avalon.tvpaint.lib import ( + from openpype.hosts.tvpaint.api.lib import ( get_layers_data, get_groups_data, get_layers_pre_post_behavior, From 21b66991b7aa2418b71423a7e541c2ddf764459e Mon Sep 17 00:00:00 2001 From: BenoitConnan Date: Mon, 3 Jan 2022 15:52:17 +0100 Subject: [PATCH 331/492] add "attach_to_root" option as a qargparse argument --- openpype/hosts/maya/api/plugin.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/openpype/hosts/maya/api/plugin.py b/openpype/hosts/maya/api/plugin.py index fdad0e09899..a5f03cd576e 100644 --- a/openpype/hosts/maya/api/plugin.py +++ b/openpype/hosts/maya/api/plugin.py @@ -100,6 +100,13 @@ class ReferenceLoader(api.Loader): "offset", label="Position Offset", help="Offset loaded models for easier selection." + ), + qargparse.Boolean( + "attach_to_root", + label="Group imported asset", + default=True, + help="Should a group be created to encapsulate" + " imported representation ?" ) ] From 9da05a2a2e21605f21cb5f9deef06cbf886e4db9 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 4 Jan 2022 10:36:42 +0100 Subject: [PATCH 332/492] make dialog shown in create render layer always on top --- openpype/hosts/tvpaint/plugins/create/create_render_layer.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openpype/hosts/tvpaint/plugins/create/create_render_layer.py b/openpype/hosts/tvpaint/plugins/create/create_render_layer.py index c43c136f3e1..40a7d159903 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render_layer.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render_layer.py @@ -197,6 +197,7 @@ def process(self): ) def _ask_user_subset_override(self, instance): + from Qt import QtCore from Qt.QtWidgets import QMessageBox title = "Subset \"{}\" already exist".format(instance["subset"]) @@ -206,6 +207,10 @@ def _ask_user_subset_override(self, instance): ).format(instance["subset"]) dialog = QMessageBox() + dialog.setWindowFlags( + dialog.windowFlags() + | QtCore.Qt.WindowStaysOnTopHint + ) dialog.setWindowTitle(title) dialog.setText(text) dialog.setStandardButtons(QMessageBox.Yes | QMessageBox.No) From 3ca931097d836bb1b5e49c15dd04291be2bb485d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 4 Jan 2022 10:57:21 +0100 Subject: [PATCH 333/492] source and proxy models can be changed in assets widget --- openpype/tools/utils/assets_widget.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/openpype/tools/utils/assets_widget.py b/openpype/tools/utils/assets_widget.py index f310aafe890..b73aca8d633 100644 --- a/openpype/tools/utils/assets_widget.py +++ b/openpype/tools/utils/assets_widget.py @@ -582,11 +582,8 @@ def __init__(self, dbcon, parent=None): self.dbcon = dbcon # Tree View - model = AssetModel(dbcon=self.dbcon, parent=self) - proxy = RecursiveSortFilterProxyModel() - proxy.setSourceModel(model) - proxy.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive) - proxy.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive) + model = self._create_source_model() + proxy = self._create_proxy_model(model) view = AssetsView(self) view.setModel(proxy) @@ -628,7 +625,6 @@ def __init__(self, dbcon, parent=None): selection_model.selectionChanged.connect(self._on_selection_change) refresh_btn.clicked.connect(self.refresh) current_asset_btn.clicked.connect(self.set_current_session_asset) - model.refreshed.connect(self._on_model_refresh) view.doubleClicked.connect(self.double_clicked) self._current_asset_btn = current_asset_btn @@ -639,6 +635,18 @@ def __init__(self, dbcon, parent=None): self.model_selection = {} + def _create_source_model(self): + model = AssetModel(dbcon=self.dbcon, parent=self) + model.refreshed.connect(self._on_model_refresh) + return model + + def _create_proxy_model(self, source_model): + proxy = RecursiveSortFilterProxyModel() + proxy.setSourceModel(source_model) + proxy.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive) + proxy.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive) + return proxy + @property def refreshing(self): return self._model.refreshing @@ -691,6 +699,12 @@ def _on_filter_text_change(self, new_text): self._proxy.setFilterFixedString(new_text) def _on_model_refresh(self, has_item): + """This method should be triggered on model refresh. + + Default implementation register this callback in '_create_source_model' + so if you're modifying model keep in mind that this method should be + called when refresh is done. + """ self._proxy.sort(0) self._set_loading_state(loading=False, empty=not has_item) self.refreshed.emit() From 03fa4e7443fa0e4db7b8b48ba4e82c77ccba3d5f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 4 Jan 2022 10:57:59 +0100 Subject: [PATCH 334/492] got rid of 'clear' argument in refresh method of assets widget --- openpype/tools/utils/assets_widget.py | 36 ++++++++++++++++----------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/openpype/tools/utils/assets_widget.py b/openpype/tools/utils/assets_widget.py index b73aca8d633..1495586b040 100644 --- a/openpype/tools/utils/assets_widget.py +++ b/openpype/tools/utils/assets_widget.py @@ -306,6 +306,8 @@ def __init__(self, dbcon, parent=None): self._items_with_color_by_id = {} self._items_by_asset_id = {} + self._last_project_name = None + @property def refreshing(self): return self._refreshing @@ -347,7 +349,7 @@ def get_indexes_by_asset_names(self, asset_names): return self.get_indexes_by_asset_ids(asset_ids) - def refresh(self, force=False, clear=False): + def refresh(self, force=False): """Refresh the data for the model. Args: @@ -360,7 +362,13 @@ def refresh(self, force=False, clear=False): return self.stop_refresh() - if clear: + project_name = self.dbcon.Session.get("AVALON_PROJECT") + clear_model = False + if project_name != self._last_project_name: + clear_model = True + self._last_project_name = project_name + + if clear_model: self._clear_items() # Fetch documents from mongo @@ -401,11 +409,18 @@ def _on_docs_fetched(self): self._clear_items() return + self._fill_assets(self._doc_payload) + + self.refreshed.emit(bool(self._items_by_asset_id)) + + self._stop_fetch_thread() + + def _fill_assets(self, asset_docs): # Collect asset documents as needed asset_ids = set() asset_docs_by_id = {} asset_ids_by_parents = collections.defaultdict(set) - for asset_doc in self._doc_payload: + for asset_doc in asset_docs: asset_id = asset_doc["_id"] asset_data = asset_doc.get("data") or {} parent_id = asset_data.get("visualParent") @@ -511,10 +526,6 @@ def _on_docs_fetched(self): except Exception: pass - self.refreshed.emit(bool(self._items_by_asset_id)) - - self._stop_fetch_thread() - def _threaded_fetch(self): asset_docs = self._fetch_asset_docs() if not self._refreshing: @@ -652,12 +663,7 @@ def refreshing(self): return self._model.refreshing def refresh(self): - project_name = self.dbcon.Session.get("AVALON_PROJECT") - clear_model = False - if project_name != self._last_project_name: - clear_model = True - self._last_project_name = project_name - self._refresh_model(clear_model) + self._refresh_model() def stop_refresh(self): self._model.stop_refresh() @@ -709,14 +715,14 @@ def _on_model_refresh(self, has_item): self._set_loading_state(loading=False, empty=not has_item) self.refreshed.emit() - def _refresh_model(self, clear=False): + def _refresh_model(self): # Store selection self._set_loading_state(loading=True, empty=True) # Trigger signal before refresh is called self.refresh_triggered.emit() # Refresh model - self._model.refresh(clear=clear) + self._model.refresh() def _set_loading_state(self, loading, empty): self._view.set_loading_state(loading, empty) From e389067ed26a773b023245900d6edf84331439ad Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 4 Jan 2022 10:59:57 +0100 Subject: [PATCH 335/492] tasks widget have ability to create different tasks model --- openpype/tools/utils/tasks_widget.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/openpype/tools/utils/tasks_widget.py b/openpype/tools/utils/tasks_widget.py index 419e77c780f..6e6cd17ffd1 100644 --- a/openpype/tools/utils/tasks_widget.py +++ b/openpype/tools/utils/tasks_widget.py @@ -194,6 +194,8 @@ class TasksWidget(QtWidgets.QWidget): task_changed = QtCore.Signal() def __init__(self, dbcon, parent=None): + self._dbcon = dbcon + super(TasksWidget, self).__init__(parent) tasks_view = DeselectableTreeView(self) @@ -204,9 +206,8 @@ def __init__(self, dbcon, parent=None): header_view = tasks_view.header() header_view.setSortIndicator(0, QtCore.Qt.AscendingOrder) - tasks_model = TasksModel(dbcon) - tasks_proxy = TasksProxyModel() - tasks_proxy.setSourceModel(tasks_model) + tasks_model = self._create_source_model() + tasks_proxy = self._create_proxy_model(tasks_model) tasks_view.setModel(tasks_proxy) layout = QtWidgets.QVBoxLayout(self) @@ -222,6 +223,19 @@ def __init__(self, dbcon, parent=None): self._last_selected_task_name = None + def _create_source_model(self): + """Create source model of tasks widget. + + Model must have available 'refresh' method and 'set_asset_id' to change + context of asset. + """ + return TasksModel(self._dbcon) + + def _create_proxy_model(self, source_model): + proxy = TasksProxyModel() + proxy.setSourceModel(source_model) + return proxy + def refresh(self): self._tasks_model.refresh() From 5d611395dfc34dd40192f6c3faf97f026c3bd042 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 4 Jan 2022 12:21:34 +0100 Subject: [PATCH 336/492] improve handling with selection --- openpype/hosts/flame/api/menu.py | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/openpype/hosts/flame/api/menu.py b/openpype/hosts/flame/api/menu.py index 893c7a21d05..fef6dbfa355 100644 --- a/openpype/hosts/flame/api/menu.py +++ b/openpype/hosts/flame/api/menu.py @@ -25,11 +25,11 @@ } -def send_selection(selection): +def callback_selection(selection, function): import openpype.hosts.flame as opflame opflame.selection = selection print(opflame.selection) - return True + function() class _FlameMenuApp(object): @@ -103,9 +103,6 @@ def build_menu(self): if not self.flame: return [] - flame_project_name = self.flame_project_name - self.log.info("______ {} ______".format(flame_project_name)) - menu = deepcopy(self.menu) menu['actions'].append({ @@ -114,13 +111,13 @@ def build_menu(self): }) menu['actions'].append({ "name": "Create ...", - "isVisible": send_selection, - "execute": lambda x: self.tools_helper.show_creator() + "execute": lambda x: callback_selection( + x, self.tools_helper.show_creator) }) menu['actions'].append({ "name": "Publish ...", - "isVisible": send_selection, - "execute": lambda x: self.tools_helper.show_publish() + "execute": lambda x: callback_selection( + x, self.tools_helper.show_publish) }) menu['actions'].append({ "name": "Load ...", @@ -170,20 +167,17 @@ def build_menu(self): if not self.flame: return [] - flame_project_name = self.flame_project_name - self.log.info("______ {} ______".format(flame_project_name)) - menu = deepcopy(self.menu) menu['actions'].append({ "name": "Create ...", - "isVisible": send_selection, - "execute": lambda x: self.tools_helper.show_creator() + "execute": lambda x: callback_selection( + x, self.tools_helper.show_creator) }) menu['actions'].append({ "name": "Publish ...", - "isVisible": send_selection, - "execute": lambda x: self.tools_helper.show_publish() + "execute": lambda x: callback_selection( + x, self.tools_helper.show_publish) }) menu['actions'].append({ "name": "Load ...", From b46a200ac81967469efe9a1ead789439511c07b9 Mon Sep 17 00:00:00 2001 From: OpenPype Date: Tue, 4 Jan 2022 12:51:18 +0000 Subject: [PATCH 337/492] [Automated] Bump version --- CHANGELOG.md | 12 ++++++------ openpype/version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef6c04c06fc..36e5672c3f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## [3.7.0-nightly.13](https://github.com/pypeclub/OpenPype/tree/HEAD) +## [3.7.0-nightly.14](https://github.com/pypeclub/OpenPype/tree/HEAD) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.6.4...HEAD) @@ -10,12 +10,15 @@ **🚀 Enhancements** +- General: Workdir extra folders [\#2462](https://github.com/pypeclub/OpenPype/pull/2462) +- Harmony: Added new style validations for New Publisher [\#2434](https://github.com/pypeclub/OpenPype/pull/2434) - Photoshop: New style validations for New publisher [\#2429](https://github.com/pypeclub/OpenPype/pull/2429) - General: Environment variables groups [\#2424](https://github.com/pypeclub/OpenPype/pull/2424) - Unreal: Dynamic menu created in Python [\#2422](https://github.com/pypeclub/OpenPype/pull/2422) - Settings UI: Hyperlinks to settings [\#2420](https://github.com/pypeclub/OpenPype/pull/2420) - Modules: JobQueue module moved one hierarchy level higher [\#2419](https://github.com/pypeclub/OpenPype/pull/2419) - TimersManager: Start timer post launch hook [\#2418](https://github.com/pypeclub/OpenPype/pull/2418) +- General: Run applications as separate processes under linux [\#2408](https://github.com/pypeclub/OpenPype/pull/2408) - Ftrack: Check existence of object type on recreation [\#2404](https://github.com/pypeclub/OpenPype/pull/2404) - Enhancement: Global cleanup plugin that explicitly remove paths from context [\#2402](https://github.com/pypeclub/OpenPype/pull/2402) - General: MongoDB ability to specify replica set groups [\#2401](https://github.com/pypeclub/OpenPype/pull/2401) @@ -24,13 +27,13 @@ - Enhancement: Settings: Use project settings values from another project [\#2382](https://github.com/pypeclub/OpenPype/pull/2382) - Blender 3: Support auto install for new blender version [\#2377](https://github.com/pypeclub/OpenPype/pull/2377) - Maya add render image path to settings [\#2375](https://github.com/pypeclub/OpenPype/pull/2375) -- Settings: Webpublisher in hosts enum [\#2367](https://github.com/pypeclub/OpenPype/pull/2367) - Hiero: python3 compatibility [\#2365](https://github.com/pypeclub/OpenPype/pull/2365) -- Burnins: Be able recognize mxf OPAtom format [\#2361](https://github.com/pypeclub/OpenPype/pull/2361) - Maya: Add is\_static\_image\_plane and is\_in\_all\_views option in imagePlaneLoader [\#2356](https://github.com/pypeclub/OpenPype/pull/2356) +- TVPaint: Move implementation to OpenPype [\#2336](https://github.com/pypeclub/OpenPype/pull/2336) **🐛 Bug fixes** +- TVPaint: Create render layer dialog is in front [\#2471](https://github.com/pypeclub/OpenPype/pull/2471) - Short Pyblish plugin path [\#2428](https://github.com/pypeclub/OpenPype/pull/2428) - PS: Introduced settings for invalid characters to use in ValidateNaming plugin [\#2417](https://github.com/pypeclub/OpenPype/pull/2417) - Settings UI: Breadcrumbs path does not create new entities [\#2416](https://github.com/pypeclub/OpenPype/pull/2416) @@ -45,10 +48,7 @@ - Nuke: fixing menu re-drawing during context change [\#2374](https://github.com/pypeclub/OpenPype/pull/2374) - Webpublisher: Fix assignment of families of TVpaint instances [\#2373](https://github.com/pypeclub/OpenPype/pull/2373) - Nuke: fixing node name based on switched asset name [\#2369](https://github.com/pypeclub/OpenPype/pull/2369) -- JobQueue: Fix loading of settings [\#2362](https://github.com/pypeclub/OpenPype/pull/2362) -- Tools: Placeholder color [\#2359](https://github.com/pypeclub/OpenPype/pull/2359) - Houdini: Fix HDA creation [\#2350](https://github.com/pypeclub/OpenPype/pull/2350) -- Flame: Fix default argument value in custom dictionary [\#2339](https://github.com/pypeclub/OpenPype/pull/2339) **Merged pull requests:** diff --git a/openpype/version.py b/openpype/version.py index 891cd4fd52b..2d238f68b6a 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.7.0-nightly.13" +__version__ = "3.7.0-nightly.14" diff --git a/pyproject.toml b/pyproject.toml index adf3e3a25da..d027f3f735d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.7.0-nightly.13" # OpenPype +version = "3.7.0-nightly.14" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From b8a5b1636e5bfdb1d494d63b4734b5ccbda7fa9c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 4 Jan 2022 13:57:05 +0100 Subject: [PATCH 338/492] removed unused avalon_mongo_url from module --- openpype/modules/avalon_apps/avalon_app.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/openpype/modules/avalon_apps/avalon_app.py b/openpype/modules/avalon_apps/avalon_app.py index 9e650a097e2..347fcf11a68 100644 --- a/openpype/modules/avalon_apps/avalon_app.py +++ b/openpype/modules/avalon_apps/avalon_app.py @@ -13,14 +13,6 @@ def initialize(self, modules_settings): avalon_settings = modules_settings[self.name] - # Check if environment is already set - avalon_mongo_url = os.environ.get("AVALON_MONGO") - if not avalon_mongo_url: - avalon_mongo_url = avalon_settings["AVALON_MONGO"] - # Use pype mongo if Avalon's mongo not defined - if not avalon_mongo_url: - avalon_mongo_url = os.environ["OPENPYPE_MONGO"] - thumbnail_root = os.environ.get("AVALON_THUMBNAIL_ROOT") if not thumbnail_root: thumbnail_root = avalon_settings["AVALON_THUMBNAIL_ROOT"] @@ -31,7 +23,6 @@ def initialize(self, modules_settings): avalon_mongo_timeout = avalon_settings["AVALON_TIMEOUT"] self.thumbnail_root = thumbnail_root - self.avalon_mongo_url = avalon_mongo_url self.avalon_mongo_timeout = avalon_mongo_timeout # Tray attributes From 4a2b1865bf6f11e1255d6cf0d19e67b0fe3dc773 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 4 Jan 2022 13:57:39 +0100 Subject: [PATCH 339/492] library loader is not always on top in tray --- openpype/modules/avalon_apps/avalon_app.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/openpype/modules/avalon_apps/avalon_app.py b/openpype/modules/avalon_apps/avalon_app.py index 347fcf11a68..51a22323f19 100644 --- a/openpype/modules/avalon_apps/avalon_app.py +++ b/openpype/modules/avalon_apps/avalon_app.py @@ -42,12 +42,20 @@ def get_global_environments(self): def tray_init(self): # Add library tool try: + from Qt import QtCore from openpype.tools.libraryloader import LibraryLoaderWindow - self.libraryloader = LibraryLoaderWindow( + libraryloader = LibraryLoaderWindow( show_projects=True, show_libraries=True ) + # Remove always on top flag for tray + window_flags = libraryloader.windowFlags() + if window_flags | QtCore.Qt.WindowStaysOnTopHint: + window_flags ^= QtCore.Qt.WindowStaysOnTopHint + libraryloader.setWindowFlags(window_flags) + self.libraryloader = libraryloader + except Exception: self.log.warning( "Couldn't load Library loader tool for tray.", From 80fd24fed5ad4ffcd99a6de8495b78efce6fc85d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 4 Jan 2022 13:57:55 +0100 Subject: [PATCH 340/492] use openpype style in delivery loader --- openpype/plugins/load/delivery.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/load/delivery.py b/openpype/plugins/load/delivery.py index a8cb0070ee5..90f8e91571d 100644 --- a/openpype/plugins/load/delivery.py +++ b/openpype/plugins/load/delivery.py @@ -3,11 +3,11 @@ from Qt import QtWidgets, QtCore, QtGui -from avalon import api, style +from avalon import api from avalon.api import AvalonMongoDB from openpype.api import Anatomy, config -from openpype import resources +from openpype import resources, style from openpype.lib.delivery import ( sizeof_fmt, From d53b038b4abf928f07527139043143b2b831eaef Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 4 Jan 2022 13:58:14 +0100 Subject: [PATCH 341/492] modified flags of delivery dialog to be always on top --- openpype/plugins/load/delivery.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/openpype/plugins/load/delivery.py b/openpype/plugins/load/delivery.py index 90f8e91571d..1037d6dc165 100644 --- a/openpype/plugins/load/delivery.py +++ b/openpype/plugins/load/delivery.py @@ -1,5 +1,5 @@ -from collections import defaultdict import copy +from collections import defaultdict from Qt import QtWidgets, QtCore, QtGui @@ -58,6 +58,18 @@ class DeliveryOptionsDialog(QtWidgets.QDialog): def __init__(self, contexts, log=None, parent=None): super(DeliveryOptionsDialog, self).__init__(parent=parent) + self.setWindowTitle("OpenPype - Deliver versions") + icon = QtGui.QIcon(resources.get_openpype_icon_filepath()) + self.setWindowIcon(icon) + + self.setWindowFlags( + QtCore.Qt.WindowStaysOnTopHint + | QtCore.Qt.WindowCloseButtonHint + | QtCore.Qt.WindowMinimizeButtonHint + ) + + self.setStyleSheet(style.load_stylesheet()) + project = contexts[0]["project"]["name"] self.anatomy = Anatomy(project) self._representations = None @@ -70,16 +82,6 @@ def __init__(self, contexts, log=None, parent=None): self._set_representations(contexts) - self.setWindowTitle("OpenPype - Deliver versions") - icon = QtGui.QIcon(resources.get_openpype_icon_filepath()) - self.setWindowIcon(icon) - - self.setWindowFlags( - QtCore.Qt.WindowCloseButtonHint | - QtCore.Qt.WindowMinimizeButtonHint - ) - self.setStyleSheet(style.load_stylesheet()) - dropdown = QtWidgets.QComboBox() self.templates = self._get_templates(self.anatomy) for name, _ in self.templates.items(): From 53971b55e3daf41867879fb0305ed70059166af2 Mon Sep 17 00:00:00 2001 From: OpenPype Date: Tue, 4 Jan 2022 13:39:41 +0000 Subject: [PATCH 342/492] [Automated] Release --- CHANGELOG.md | 9 ++------- openpype/version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 36e5672c3f6..fc14b5f507c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,17 +1,12 @@ # Changelog -## [3.7.0-nightly.14](https://github.com/pypeclub/OpenPype/tree/HEAD) +## [3.7.0](https://github.com/pypeclub/OpenPype/tree/3.7.0) (2022-01-04) -[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.6.4...HEAD) - -**Deprecated:** - -- General: Default modules hierarchy n2 [\#2368](https://github.com/pypeclub/OpenPype/pull/2368) +[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.6.4...3.7.0) **🚀 Enhancements** - General: Workdir extra folders [\#2462](https://github.com/pypeclub/OpenPype/pull/2462) -- Harmony: Added new style validations for New Publisher [\#2434](https://github.com/pypeclub/OpenPype/pull/2434) - Photoshop: New style validations for New publisher [\#2429](https://github.com/pypeclub/OpenPype/pull/2429) - General: Environment variables groups [\#2424](https://github.com/pypeclub/OpenPype/pull/2424) - Unreal: Dynamic menu created in Python [\#2422](https://github.com/pypeclub/OpenPype/pull/2422) diff --git a/openpype/version.py b/openpype/version.py index 2d238f68b6a..8fac77bcdf6 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.7.0-nightly.14" +__version__ = "3.7.0" diff --git a/pyproject.toml b/pyproject.toml index d027f3f735d..dd1f5c90b6b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.7.0-nightly.14" # OpenPype +version = "3.7.0" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From 31ab8e51f88e559fadade204c247682a0a7fbf3a Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 4 Jan 2022 15:31:28 +0100 Subject: [PATCH 343/492] marker colors picking correctly --- openpype/hosts/flame/otio/flame_export.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/flame/otio/flame_export.py b/openpype/hosts/flame/otio/flame_export.py index 356cf1b49e8..aea1f387e84 100644 --- a/openpype/hosts/flame/otio/flame_export.py +++ b/openpype/hosts/flame/otio/flame_export.py @@ -3,7 +3,6 @@ import os import re -import sys import json import logging import opentimelineio as otio @@ -52,7 +51,7 @@ def set_fps(cls, new_fps): @classmethod def get_fps(cls): - return cls._fps + return cls._fps @classmethod def set_tl_start_frame(cls, number): @@ -64,7 +63,7 @@ def set_tl_start_frame(cls, number): @classmethod def get_tl_start_frame(cls): - return cls._tl_start_frame + return cls._tl_start_frame def flatten(_list): @@ -105,7 +104,6 @@ def _get_metadata(item): def create_time_effects(otio_clip, item): # todo #2426: add retiming effects to export - pass # get all subtrack items # subTrackItems = flatten(track_item.parent().subTrackItems()) # speed = track_item.playbackSpeed() @@ -174,11 +172,18 @@ def create_time_effects(otio_clip, item): # # add otio effect to clip effects # otio_clip.effects.append(otio_effect) + pass def _get_marker_color(flame_colour): - if flame_colour in MARKERS_COLOR_MAP: - return MARKERS_COLOR_MAP[flame_colour] + # clamp colors to closes half numbers + _flame_colour = [ + (lambda x: round(x * 2) / 2)(c) + for c in flame_colour] + + for color, otio_color_type in MARKERS_COLOR_MAP.items(): + if _flame_colour == list(color): + return otio_color_type return otio.schema.MarkerColor.RED From cd0ad34595286cd06fd48c970713f4beba0d8a86 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 4 Jan 2022 16:34:10 +0100 Subject: [PATCH 344/492] modify comment Co-authored-by: Petr Kalis --- openpype/settings/entities/op_version_entity.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/openpype/settings/entities/op_version_entity.py b/openpype/settings/entities/op_version_entity.py index 6f6243cfeef..55f8450edfc 100644 --- a/openpype/settings/entities/op_version_entity.py +++ b/openpype/settings/entities/op_version_entity.py @@ -14,10 +14,8 @@ class OpenPypeVersionInput(TextEntity): """Entity to store OpenPype version to use. - It is text input as creating of settings on different machines may - affect which versions are available so it must have option to set OpenPype - version which is not available for machine where settings entities are - loaded. + Settings created on another machine may affect available versions on current user's machine. + Text input element is provided to explicitly set version not yet showing up the user's machine. It is possible to enter empty string. In that case is used any latest version. Any other string must match regex of OpenPype version semantic. From 18104b6aed36ee490992e76fe11b0b02b8d7ef1f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 4 Jan 2022 16:54:48 +0100 Subject: [PATCH 345/492] fix docstring text length --- openpype/settings/entities/op_version_entity.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/settings/entities/op_version_entity.py b/openpype/settings/entities/op_version_entity.py index 55f8450edfc..1576173654a 100644 --- a/openpype/settings/entities/op_version_entity.py +++ b/openpype/settings/entities/op_version_entity.py @@ -14,8 +14,9 @@ class OpenPypeVersionInput(TextEntity): """Entity to store OpenPype version to use. - Settings created on another machine may affect available versions on current user's machine. - Text input element is provided to explicitly set version not yet showing up the user's machine. + Settings created on another machine may affect available versions + on current user's machine. Text input element is provided to explicitly + set version not yet showing up the user's machine. It is possible to enter empty string. In that case is used any latest version. Any other string must match regex of OpenPype version semantic. From 07e151981c34e8cdb5710ce8a853be08530f0cb9 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 4 Jan 2022 17:14:00 +0100 Subject: [PATCH 346/492] skip duplicated versions --- openpype/settings/entities/op_version_entity.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/openpype/settings/entities/op_version_entity.py b/openpype/settings/entities/op_version_entity.py index 1576173654a..6184f7640a8 100644 --- a/openpype/settings/entities/op_version_entity.py +++ b/openpype/settings/entities/op_version_entity.py @@ -38,9 +38,10 @@ def set_override_state(self, state, *args, **kwargs): value_hints = [] if state is OverrideState.STUDIO: versions = self._get_openpype_versions() - if versions is not None: - for version in versions: - value_hints.append(str(version)) + for version in versions: + version_str = str(version) + if version_str not in value_hints: + value_hints.append(version_str) self.value_hints = value_hints From ef87a1f086adf20d57f9284b33253f7b04d38b75 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 4 Jan 2022 17:14:43 +0100 Subject: [PATCH 347/492] confirmation dialog requires to write whole project name --- .../project_manager/project_manager/widgets.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/openpype/tools/project_manager/project_manager/widgets.py b/openpype/tools/project_manager/project_manager/widgets.py index e4c58a8a2cf..392f3f45039 100644 --- a/openpype/tools/project_manager/project_manager/widgets.py +++ b/openpype/tools/project_manager/project_manager/widgets.py @@ -336,18 +336,21 @@ def __init__(self, project_name, parent): self.setWindowTitle("Delete project?") - message = ( + message_label = QtWidgets.QLabel(self) + message_label.setWordWrap(True) + message_label.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) + message_label.setText(( "Project \"{}\" with all related data will be" " permanently removed from the database (This actions won't remove" " any files on disk)." - ).format(project_name) - message_label = QtWidgets.QLabel(message, self) - message_label.setWordWrap(True) + ).format(project_name)) question_label = QtWidgets.QLabel("Are you sure?", self) confirm_input = PlaceholderLineEdit(self) - confirm_input.setPlaceholderText("Type \"Delete\" to confirm...") + confirm_input.setPlaceholderText( + "Type \"{}\" to confirm...".format(project_name) + ) cancel_btn = _SameSizeBtns("Cancel", self) cancel_btn.setToolTip("Cancel deletion of the project") @@ -379,6 +382,7 @@ def __init__(self, project_name, parent): self._confirm_btn = confirm_btn self._confirm_input = confirm_input self._result = 0 + self._project_name = project_name self.setMinimumWidth(480) self.setMaximumWidth(650) @@ -411,5 +415,5 @@ def _on_enter_clicked(self): self._on_confirm_click() def _on_confirm_text_change(self): - enabled = self._confirm_input.text().lower() == "delete" + enabled = self._confirm_input.text() == self._project_name self._confirm_btn.setEnabled(enabled) From de1c043ec8819b7fd68cebc5152ea8ad60d3e754 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 4 Jan 2022 17:14:59 +0100 Subject: [PATCH 348/492] added label "Delete project" to delete button --- openpype/tools/project_manager/project_manager/window.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/tools/project_manager/project_manager/window.py b/openpype/tools/project_manager/project_manager/window.py index a05811e8136..0298d565a57 100644 --- a/openpype/tools/project_manager/project_manager/window.py +++ b/openpype/tools/project_manager/project_manager/window.py @@ -78,7 +78,9 @@ def __init__(self, parent=None): ) create_folders_btn.setEnabled(False) - remove_projects_btn = QtWidgets.QPushButton(project_widget) + remove_projects_btn = QtWidgets.QPushButton( + "Delete project", project_widget + ) remove_projects_btn.setIcon(ResourceCache.get_icon("remove")) remove_projects_btn.setObjectName("IconBtn") From 737a485530bbac1960ad4fe93bb381daea4cc956 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 4 Jan 2022 17:59:58 +0100 Subject: [PATCH 349/492] flame: fix passing env var and flame version dynamic filling --- .../hosts/flame/api/scripts/wiretap_com.py | 17 +++++++++----- openpype/hosts/flame/hooks/pre_flame_setup.py | 23 +++++++++++++------ .../system_settings/applications.json | 8 ++++--- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/openpype/hosts/flame/api/scripts/wiretap_com.py b/openpype/hosts/flame/api/scripts/wiretap_com.py index d8dc1884cf5..5f7b2580e66 100644 --- a/openpype/hosts/flame/api/scripts/wiretap_com.py +++ b/openpype/hosts/flame/api/scripts/wiretap_com.py @@ -10,14 +10,19 @@ from copy import deepcopy import datetime +FLAME_V = os.getenv("OPENPYPE_FLAME_VERSION") + +if not FLAME_V: + raise KeyError("Missing key in environment `OPENPYPE_FLAME_VERSION`") + try: from libwiretapPythonClientAPI import ( WireTapClientInit) except ImportError: - flame_python_path = "/opt/Autodesk/flame_2021/python" + flame_python_path = "/opt/Autodesk/flame_{}/python".format(FLAME_V) flame_exe_path = ( - "/opt/Autodesk/flame_2021/bin/flame.app" - "/Contents/MacOS/startApp") + "/opt/Autodesk/flame_{}/bin/flame.app" + "/Contents/MacOS/startApp").format(FLAME_V) sys.path.append(flame_python_path) @@ -169,7 +174,7 @@ def _project_prep(self, project_name): # check if volumes exists if self.volume_name not in volumes: raise AttributeError( - ("Volume '{}' does not exist '{}'").format( + ("Volume '{}' does not exist in '{}'").format( self.volume_name, volumes) ) @@ -179,7 +184,7 @@ def _project_prep(self, project_name): "/opt/Autodesk/", "wiretap", "tools", - "2021", + FLAME_V, "wiretap_create_node", ), '-n', @@ -434,7 +439,7 @@ def _set_project_colorspace(self, project_name, color_policy): "/opt/Autodesk/", "wiretap", "tools", - "2021", + FLAME_V, "wiretap_duplicate_node", ), "-s", diff --git a/openpype/hosts/flame/hooks/pre_flame_setup.py b/openpype/hosts/flame/hooks/pre_flame_setup.py index 159fb374102..e7ef8569070 100644 --- a/openpype/hosts/flame/hooks/pre_flame_setup.py +++ b/openpype/hosts/flame/hooks/pre_flame_setup.py @@ -19,7 +19,10 @@ class FlamePrelaunch(PreLaunchHook): app_groups = ["flame"] # todo: replace version number with avalon launch app version - flame_python_exe = "/opt/Autodesk/python/2021/bin/python2.7" + flame_python_exe = ( + "/opt/Autodesk/python/{OPENPYPE_FLAME_VERSION}" + "/bin/python2.7" + ) wtc_script_path = os.path.join( opflame.HOST_DIR, "api", "scripts", "wiretap_com.py") @@ -30,6 +33,7 @@ def __init__(self, *args, **kwargs): self.signature = "( {} )".format(self.__class__.__name__) def execute(self): + _env = self.launch_context.env """Hook entry method.""" project_doc = self.data["project_doc"] user_name = get_openpype_username() @@ -58,9 +62,9 @@ def execute(self): data_to_script = { # from settings - "host_name": os.getenv("FLAME_WIRETAP_HOSTNAME") or hostname, - "volume_name": os.getenv("FLAME_WIRETAP_VOLUME"), - "group_name": os.getenv("FLAME_WIRETAP_GROUP"), + "host_name": _env.get("FLAME_WIRETAP_HOSTNAME") or hostname, + "volume_name": _env.get("FLAME_WIRETAP_VOLUME"), + "group_name": _env.get("FLAME_WIRETAP_GROUP"), "color_policy": "ACES 1.1", # from project @@ -68,9 +72,12 @@ def execute(self): "user_name": user_name, "project_data": project_data } + + self.log.info(pformat(dict(_env))) + self.log.info(pformat(data_to_script)) + app_arguments = self._get_launch_arguments(data_to_script) - self.log.info(pformat(dict(self.launch_context.env))) opflame.setup(self.launch_context.env) @@ -83,7 +90,9 @@ def _get_launch_arguments(self, script_data): with make_temp_file(dumped_script_data) as tmp_json_path: # Prepare subprocess arguments args = [ - self.flame_python_exe, + self.flame_python_exe.format( + **self.launch_context.env + ), self.wtc_script_path, tmp_json_path ] @@ -91,7 +100,7 @@ def _get_launch_arguments(self, script_data): process_kwargs = { "logger": self.log, - "env": {} + "env": self.launch_context.env } openpype.api.run_subprocess(args, **process_kwargs) diff --git a/openpype/settings/defaults/system_settings/applications.json b/openpype/settings/defaults/system_settings/applications.json index 1cbe09f5768..23ea64fdc19 100644 --- a/openpype/settings/defaults/system_settings/applications.json +++ b/openpype/settings/defaults/system_settings/applications.json @@ -118,10 +118,10 @@ "executables": { "windows": [], "darwin": [ - "/opt/Autodesk/flame_2021/bin/flame.app/Contents/MacOS/startApp" + "/opt/Autodesk/flame_{OPENPYPE_FLAME_VERSION}/bin/flame.app/Contents/MacOS/startApp" ], "linux": [ - "/opt/Autodesk/flame_2021/bin/flame" + "/opt/Autodesk/flame_{OPENPYPE_FLAME_VERSION}/bin/flame" ] }, "arguments": { @@ -129,7 +129,9 @@ "darwin": [], "linux": [] }, - "environment": {} + "environment": { + "OPENPYPE_FLAME_VERSION": "2021" + } }, "__dynamic_keys_labels__": { "2021": "2021 (Testing Only)" From 571eef7cb374f87c62ed8d9be804994dde43624e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 4 Jan 2022 18:24:50 +0100 Subject: [PATCH 350/492] fix filtering criteria for profile selection --- openpype/lib/avalon_context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/lib/avalon_context.py b/openpype/lib/avalon_context.py index cb5bca133da..8180e416a92 100644 --- a/openpype/lib/avalon_context.py +++ b/openpype/lib/avalon_context.py @@ -1560,7 +1560,7 @@ def get_custom_workfile_template_by_context( # get path from matching profile matching_item = filter_profiles( template_profiles, - {"task_type": current_task_type} + {"task_types": current_task_type} ) # when path is available try to format it in case # there are some anatomy template strings From eaeb96c92f4075f2719892d732ef842eb5f36fb3 Mon Sep 17 00:00:00 2001 From: OpenPype Date: Wed, 5 Jan 2022 03:41:51 +0000 Subject: [PATCH 351/492] [Automated] Bump version --- CHANGELOG.md | 28 +++++++++++++++++++++++++++- openpype/version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc14b5f507c..c46b1f37e12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,34 @@ # Changelog +## [3.8.0-nightly.1](https://github.com/pypeclub/OpenPype/tree/HEAD) + +[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.7.0...HEAD) + +**🆕 New features** + +- Flame: OpenTimelineIO Export Modul [\#2398](https://github.com/pypeclub/OpenPype/pull/2398) + +**🚀 Enhancements** + +- Tools: Be able to change models of tasks and assets widgets [\#2475](https://github.com/pypeclub/OpenPype/pull/2475) +- Publish pype: Reduce publish process defering [\#2464](https://github.com/pypeclub/OpenPype/pull/2464) +- Maya: Improve speed of Collect History logic [\#2460](https://github.com/pypeclub/OpenPype/pull/2460) +- Maya: Validate Rig Controllers - fix Error: in script editor [\#2459](https://github.com/pypeclub/OpenPype/pull/2459) +- Maya: Optimize Validate Locked Normals speed for dense polymeshes [\#2457](https://github.com/pypeclub/OpenPype/pull/2457) +- Maya : add option to not group reference in ReferenceLoader [\#2383](https://github.com/pypeclub/OpenPype/pull/2383) + +**🐛 Bug fixes** + +- General: Custom template paths filter fix [\#2483](https://github.com/pypeclub/OpenPype/pull/2483) +- Loader: Remove always on top flag in tray [\#2480](https://github.com/pypeclub/OpenPype/pull/2480) + ## [3.7.0](https://github.com/pypeclub/OpenPype/tree/3.7.0) (2022-01-04) -[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.6.4...3.7.0) +[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.7.0-nightly.14...3.7.0) + +**Deprecated:** + +- General: Default modules hierarchy n2 [\#2368](https://github.com/pypeclub/OpenPype/pull/2368) **🚀 Enhancements** diff --git a/openpype/version.py b/openpype/version.py index 8fac77bcdf6..4f10deeae15 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.7.0" +__version__ = "3.8.0-nightly.1" diff --git a/pyproject.toml b/pyproject.toml index dd1f5c90b6b..20aaf62d064 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.7.0" # OpenPype +version = "3.8.0-nightly.1" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From 6b8b7f22e65aa964f0f9276672adcbabae8c392e Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 5 Jan 2022 10:30:31 +0100 Subject: [PATCH 352/492] Fix #2473 Update "Repair Context" label to "Repair" --- openpype/action.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/action.py b/openpype/action.py index 3fc6dd1a8f6..50741875e48 100644 --- a/openpype/action.py +++ b/openpype/action.py @@ -72,7 +72,7 @@ class RepairContextAction(pyblish.api.Action): is available on the plugin. """ - label = "Repair Context" + label = "Repair" on = "failed" # This action is only available on a failed plug-in def process(self, context, plugin): From 1c865567f5b88061aff9bda6a3cd4ef4662a6034 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 5 Jan 2022 11:12:21 +0100 Subject: [PATCH 353/492] flame path no env var --- .../defaults/system_settings/applications.json | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/openpype/settings/defaults/system_settings/applications.json b/openpype/settings/defaults/system_settings/applications.json index 23ea64fdc19..3a097d2b370 100644 --- a/openpype/settings/defaults/system_settings/applications.json +++ b/openpype/settings/defaults/system_settings/applications.json @@ -118,10 +118,10 @@ "executables": { "windows": [], "darwin": [ - "/opt/Autodesk/flame_{OPENPYPE_FLAME_VERSION}/bin/flame.app/Contents/MacOS/startApp" + "/opt/Autodesk/flame_2021/bin/flame.app/Contents/MacOS/startApp" ], "linux": [ - "/opt/Autodesk/flame_{OPENPYPE_FLAME_VERSION}/bin/flame" + "/opt/Autodesk/flame_2021/bin/flame" ] }, "arguments": { @@ -144,7 +144,10 @@ "icon": "{}/app_icons/nuke.png", "host_name": "nuke", "environment": { - "NUKE_PATH": ["{NUKE_PATH}", "{OPENPYPE_STUDIO_PLUGINS}/nuke"] + "NUKE_PATH": [ + "{NUKE_PATH}", + "{OPENPYPE_STUDIO_PLUGINS}/nuke" + ] }, "variants": { "13-0": { @@ -250,7 +253,10 @@ "icon": "{}/app_icons/nuke.png", "host_name": "nuke", "environment": { - "NUKE_PATH": ["{NUKE_PATH}", "{OPENPYPE_STUDIO_PLUGINS}/nuke"] + "NUKE_PATH": [ + "{NUKE_PATH}", + "{OPENPYPE_STUDIO_PLUGINS}/nuke" + ] }, "variants": { "13-0": { From b3a284294949189a3737ed28fac90294a8829087 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 5 Jan 2022 11:29:37 +0100 Subject: [PATCH 354/492] flame: fixing permission issue overcoming --- openpype/hosts/flame/api/utils.py | 38 +++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/openpype/hosts/flame/api/utils.py b/openpype/hosts/flame/api/utils.py index 201c7d2facb..aae102dd7e6 100644 --- a/openpype/hosts/flame/api/utils.py +++ b/openpype/hosts/flame/api/utils.py @@ -75,10 +75,19 @@ def _sync_utility_scripts(env=None): path = os.path.join(flame_shared_dir, _itm) log.info("Removing `{path}`...".format(**locals())) - if os.path.isdir(path): - shutil.rmtree(path, onerror=None) - else: - os.remove(path) + + try: + if os.path.isdir(path): + shutil.rmtree(path, onerror=None) + else: + os.remove(path) + except PermissionError as msg: + log.warning( + "Not able to remove: `{}`, Problem with: `{}`".format( + path, + msg + ) + ) # copy scripts into Resolve's utility scripts dir for dirpath, scriptlist in scripts.items(): @@ -88,13 +97,22 @@ def _sync_utility_scripts(env=None): src = os.path.join(dirpath, _script) dst = os.path.join(flame_shared_dir, _script) log.info("Copying `{src}` to `{dst}`...".format(**locals())) - if os.path.isdir(src): - shutil.copytree( - src, dst, symlinks=False, - ignore=None, ignore_dangling_symlinks=False + + try: + if os.path.isdir(src): + shutil.copytree( + src, dst, symlinks=False, + ignore=None, ignore_dangling_symlinks=False + ) + else: + shutil.copy2(src, dst) + except PermissionError as msg: + log.warning( + "Not able to coppy to: `{}`, Problem with: `{}`".format( + dst, + msg + ) ) - else: - shutil.copy2(src, dst) def setup(env=None): From 97f6afa90fb9bf6d5f4acbb1a41b0b80c730b8a8 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 5 Jan 2022 14:36:21 +0100 Subject: [PATCH 355/492] flame: fixing pref file handling --- openpype/hosts/flame/api/lib.py | 9 ++++++--- openpype/hosts/flame/api/utils.py | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index 96bffab7744..44043c00f29 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -114,11 +114,14 @@ def __init__(self): self.hostname, ) - self.log.info("[{}] waking up".format(self.__class__.__name__)) - self.load_prefs() + self.log.info("[{}] waking up".format(self.__class__.__name__)) - # menu auto-refresh defaults + try: + self.load_prefs() + except RuntimeError: + self.save_prefs() + # menu auto-refresh defaults if not self.prefs_global.get("menu_auto_refresh"): self.prefs_global["menu_auto_refresh"] = { "media_panel": True, diff --git a/openpype/hosts/flame/api/utils.py b/openpype/hosts/flame/api/utils.py index aae102dd7e6..8ed8613b15f 100644 --- a/openpype/hosts/flame/api/utils.py +++ b/openpype/hosts/flame/api/utils.py @@ -106,7 +106,7 @@ def _sync_utility_scripts(env=None): ) else: shutil.copy2(src, dst) - except PermissionError as msg: + except (PermissionError, FileExistsError) as msg: log.warning( "Not able to coppy to: `{}`, Problem with: `{}`".format( dst, From 65053ef948fc712ae939fc2756a0dc0abfed0643 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 5 Jan 2022 15:33:15 +0100 Subject: [PATCH 356/492] trigger on task changed in set context method --- openpype/tools/workfiles/app.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/tools/workfiles/app.py b/openpype/tools/workfiles/app.py index 0615ec0aca7..f4a86050cbe 100644 --- a/openpype/tools/workfiles/app.py +++ b/openpype/tools/workfiles/app.py @@ -1074,6 +1074,7 @@ def _on_context_set_timeout(self): if "task" in context: self.tasks_widget.select_task_name(context["task"]) + self._on_task_changed() def _on_asset_changed(self): asset_id = self.assets_widget.get_selected_asset_id() From d84d334ac6140a197c10ae88d6057d31bd8122e3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 5 Jan 2022 16:15:22 +0100 Subject: [PATCH 357/492] fix style and dialog modality to parent widget --- openpype/tools/loader/widgets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/tools/loader/widgets.py b/openpype/tools/loader/widgets.py index ea45fd43646..f5ade04ae9e 100644 --- a/openpype/tools/loader/widgets.py +++ b/openpype/tools/loader/widgets.py @@ -535,7 +535,7 @@ def on_context_menu(self, point): self.load_ended.emit() if error_info: - box = LoadErrorMessageBox(error_info) + box = LoadErrorMessageBox(error_info, self) box.show() def selected_subsets(self, _groups=False, _merged=False, _other=True): @@ -1431,7 +1431,7 @@ def _process_action(self, items, menu, point): self.load_ended.emit() if errors: - box = LoadErrorMessageBox(errors) + box = LoadErrorMessageBox(errors, self) box.show() def _get_optional_labels(self, loaders, selected_side): From 9d39d05e6bed847055a02a7a5f8686dcfa056545 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 5 Jan 2022 17:44:16 +0100 Subject: [PATCH 358/492] styl have function to return path to images in resources --- openpype/style/__init__.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/openpype/style/__init__.py b/openpype/style/__init__.py index cb0595d5221..ea88b342eec 100644 --- a/openpype/style/__init__.py +++ b/openpype/style/__init__.py @@ -1,8 +1,10 @@ import os import json import collections -from openpype import resources import six + +from openpype import resources + from .color_defs import parse_color @@ -12,6 +14,18 @@ current_dir = os.path.dirname(os.path.abspath(__file__)) +def get_style_image_path(image_name): + # All filenames are lowered + image_name = image_name.lower() + # Male sure filename has png extension + if not image_name.endswith(".png"): + image_name += ".png" + filepath = os.path.join(current_dir, "images", image_name) + if os.path.exists(filepath): + return filepath + return None + + def _get_colors_raw_data(): """Read data file with stylesheet fill values. @@ -160,6 +174,11 @@ def load_stylesheet(): return _STYLESHEET_CACHE -def app_icon_path(): +def get_app_icon_path(): """Path to OpenPype icon.""" return resources.get_openpype_icon_filepath() + + +def app_icon_path(): + # Backwards compatibility + return get_app_icon_path() From 13aa752c40fdfd1f61412eaea455930101b64045 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 5 Jan 2022 17:47:32 +0100 Subject: [PATCH 359/492] added clickable frame to utils --- openpype/tools/utils/__init__.py | 4 ++++ openpype/tools/utils/widgets.py | 35 ++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/openpype/tools/utils/__init__.py b/openpype/tools/utils/__init__.py index 7f15e647678..2a6d62453b9 100644 --- a/openpype/tools/utils/__init__.py +++ b/openpype/tools/utils/__init__.py @@ -1,8 +1,12 @@ from .widgets import ( PlaceholderLineEdit, + BaseClickableFrame, + ClickableFrame, ) __all__ = ( "PlaceholderLineEdit", + "BaseClickableFrame", + "ClickableFrame", ) diff --git a/openpype/tools/utils/widgets.py b/openpype/tools/utils/widgets.py index 3bfa092a210..3e45600e33c 100644 --- a/openpype/tools/utils/widgets.py +++ b/openpype/tools/utils/widgets.py @@ -25,6 +25,41 @@ def __init__(self, *args, **kwargs): self.setPalette(filter_palette) +class BaseClickableFrame(QtWidgets.QFrame): + """Widget that catch left mouse click and can trigger a callback. + + Callback is defined by overriding `_mouse_release_callback`. + """ + def __init__(self, parent): + super(BaseClickableFrame, self).__init__(parent) + + self._mouse_pressed = False + + def _mouse_release_callback(self): + pass + + def mousePressEvent(self, event): + if event.button() == QtCore.Qt.LeftButton: + self._mouse_pressed = True + super(BaseClickableFrame, self).mousePressEvent(event) + + def mouseReleaseEvent(self, event): + if self._mouse_pressed: + self._mouse_pressed = False + if self.rect().contains(event.pos()): + self._mouse_release_callback() + + super(BaseClickableFrame, self).mouseReleaseEvent(event) + + +class ClickableFrame(BaseClickableFrame): + """Extended clickable frame which triggers 'clicked' signal.""" + clicked = QtCore.Signal() + + def _mouse_release_callback(self): + self.clicked.emit() + + class ImageButton(QtWidgets.QPushButton): """PushButton with icon and size of font. From b2f09495c49fda626a30ef6c1fd21d8918b9ea45 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 5 Jan 2022 17:47:54 +0100 Subject: [PATCH 360/492] implemented expand button showing icon and having clicked signal --- openpype/tools/utils/__init__.py | 2 + openpype/tools/utils/widgets.py | 64 +++++++++++++++++++++++++++++++- 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/openpype/tools/utils/__init__.py b/openpype/tools/utils/__init__.py index 2a6d62453b9..294b919b5c8 100644 --- a/openpype/tools/utils/__init__.py +++ b/openpype/tools/utils/__init__.py @@ -2,6 +2,7 @@ PlaceholderLineEdit, BaseClickableFrame, ClickableFrame, + ExpandBtn, ) @@ -9,4 +10,5 @@ "PlaceholderLineEdit", "BaseClickableFrame", "ClickableFrame", + "ExpandBtn" ) diff --git a/openpype/tools/utils/widgets.py b/openpype/tools/utils/widgets.py index 3e45600e33c..c32eae043eb 100644 --- a/openpype/tools/utils/widgets.py +++ b/openpype/tools/utils/widgets.py @@ -3,7 +3,10 @@ from Qt import QtWidgets, QtCore, QtGui from avalon.vendor import qtawesome, qargparse -from openpype.style import get_objected_colors +from openpype.style import ( + get_objected_colors, + get_style_image_path +) log = logging.getLogger(__name__) @@ -60,6 +63,65 @@ def _mouse_release_callback(self): self.clicked.emit() +class ExpandBtnLabel(QtWidgets.QLabel): + """Label showing expand icon meant for ExpandBtn.""" + def __init__(self, parent): + super(ExpandBtnLabel, self).__init__(parent) + self._source_collapsed_pix = QtGui.QPixmap( + get_style_image_path("branch_closed") + ) + self._source_expanded_pix = QtGui.QPixmap( + get_style_image_path("branch_open") + ) + + self._current_image = self._source_collapsed_pix + self._collapsed = True + + def set_collapsed(self, collapsed): + if self._collapsed == collapsed: + return + self._collapsed = collapsed + if collapsed: + self._current_image = self._source_collapsed_pix + else: + self._current_image = self._source_expanded_pix + self._set_resized_pix() + + def resizeEvent(self, event): + self._set_resized_pix() + super(ExpandBtnLabel, self).resizeEvent(event) + + def _set_resized_pix(self): + size = int(self.fontMetrics().height() / 2) + if size < 1: + size = 1 + size += size % 2 + self.setPixmap( + self._current_image.scaled( + size, + size, + QtCore.Qt.KeepAspectRatio, + QtCore.Qt.SmoothTransformation + ) + ) + + +class ExpandBtn(ClickableFrame): + def __init__(self, parent=None): + super(ExpandBtn, self).__init__(parent) + + pixmap_label = ExpandBtnLabel(self) + + layout = QtWidgets.QHBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.addWidget(pixmap_label) + + self._pixmap_label = pixmap_label + + def set_collapsed(self, collapsed): + self._pixmap_label.set_collapsed(collapsed) + + class ImageButton(QtWidgets.QPushButton): """PushButton with icon and size of font. From 8ef9f44854bdd1e2b6001148eb4714d41dc9fee6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 5 Jan 2022 17:49:11 +0100 Subject: [PATCH 361/492] implemented traceback widget showin traceback in loader --- openpype/tools/loader/widgets.py | 51 ++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/openpype/tools/loader/widgets.py b/openpype/tools/loader/widgets.py index f5ade04ae9e..c8e371ee6f5 100644 --- a/openpype/tools/loader/widgets.py +++ b/openpype/tools/loader/widgets.py @@ -18,6 +18,8 @@ ) from openpype.tools.utils.widgets import ( OptionalMenu, + ClickableFrame, + ExpandBtn, PlaceholderLineEdit ) from openpype.tools.utils.views import ( @@ -64,6 +66,55 @@ def set_label(self, label): self.label_widget.setText(label) +class TracebackWidget(QtWidgets.QWidget): + def __init__(self, tb_text, parent): + super(TracebackWidget, self).__init__(parent) + + # Modify text to match html + # - add more replacements when needed + tb_text = ( + tb_text + .replace("<", "<") + .replace(">", ">") + .replace("\n", "
") + .replace(" ", " ") + ) + + expand_btn = ExpandBtn(self) + + clickable_frame = ClickableFrame(self) + clickable_layout = QtWidgets.QHBoxLayout(clickable_frame) + clickable_layout.setContentsMargins(0, 0, 0, 0) + + expand_label = QtWidgets.QLabel("Details", clickable_frame) + clickable_layout.addWidget(expand_label, 0) + clickable_layout.addStretch(1) + + show_details_layout = QtWidgets.QHBoxLayout() + show_details_layout.addWidget(expand_btn, 0) + show_details_layout.addWidget(clickable_frame, 1) + + text_widget = QtWidgets.QLabel(self) + text_widget.setText(tb_text) + text_widget.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) + text_widget.setVisible(False) + + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.addLayout(show_details_layout, 0) + layout.addWidget(text_widget, 1) + + clickable_frame.clicked.connect(self._on_show_details_click) + expand_btn.clicked.connect(self._on_show_details_click) + + self._expand_btn = expand_btn + self._text_widget = text_widget + + def _on_show_details_click(self): + self._text_widget.setVisible(not self._text_widget.isVisible()) + self._expand_btn.set_collapsed(not self._text_widget.isVisible()) + + class LoadErrorMessageBox(QtWidgets.QDialog): def __init__(self, messages, parent=None): super(LoadErrorMessageBox, self).__init__(parent) From 72503a16e70c57b8ada5404a88f513afaa2f9161 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 5 Jan 2022 17:50:14 +0100 Subject: [PATCH 362/492] reorganized contetn of error dialog --- openpype/tools/loader/widgets.py | 62 ++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/openpype/tools/loader/widgets.py b/openpype/tools/loader/widgets.py index c8e371ee6f5..80c4bade082 100644 --- a/openpype/tools/loader/widgets.py +++ b/openpype/tools/loader/widgets.py @@ -121,13 +121,10 @@ def __init__(self, messages, parent=None): self.setWindowTitle("Loading failed") self.setFocusPolicy(QtCore.Qt.StrongFocus) - body_layout = QtWidgets.QVBoxLayout(self) - main_label = ( "Failed to load items" ) main_label_widget = QtWidgets.QLabel(main_label, self) - body_layout.addWidget(main_label_widget) item_name_template = ( "Subset: {}
" @@ -136,38 +133,57 @@ def __init__(self, messages, parent=None): ) exc_msg_template = "{}" - for exc_msg, tb, repre, subset, version in messages: + content_scroll = QtWidgets.QScrollArea(self) + content_scroll.setWidgetResizable(True) + + content_widget = QtWidgets.QWidget(content_scroll) + content_scroll.setWidget(content_widget) + + content_layout = QtWidgets.QVBoxLayout(content_widget) + content_layout.setContentsMargins(0, 0, 0, 0) + + for exc_msg, tb_text, repre, subset, version in messages: line = self._create_line() - body_layout.addWidget(line) + content_layout.addWidget(line) item_name = item_name_template.format(subset, version, repre) item_name_widget = QtWidgets.QLabel( item_name.replace("\n", "
"), self ) - body_layout.addWidget(item_name_widget) + item_name_widget.setWordWrap(True) + content_layout.addWidget(item_name_widget) exc_msg = exc_msg_template.format(exc_msg.replace("\n", "
")) message_label_widget = QtWidgets.QLabel(exc_msg, self) - body_layout.addWidget(message_label_widget) + message_label_widget.setWordWrap(True) + content_layout.addWidget(message_label_widget) - if tb: - tb_widget = QtWidgets.QLabel(tb.replace("\n", "
"), self) - tb_widget.setTextInteractionFlags( - QtCore.Qt.TextBrowserInteraction - ) - body_layout.addWidget(tb_widget) + if tb_text: + line = self._create_line() + tb_widget = TracebackWidget(tb_text, self) + content_layout.addWidget(line) + content_layout.addWidget(tb_widget) - footer_widget = QtWidgets.QWidget(self) - footer_layout = QtWidgets.QHBoxLayout(footer_widget) - buttonBox = QtWidgets.QDialogButtonBox(QtCore.Qt.Vertical) - buttonBox.setStandardButtons( - QtWidgets.QDialogButtonBox.StandardButton.Ok - ) - buttonBox.accepted.connect(self._on_accept) - footer_layout.addWidget(buttonBox, alignment=QtCore.Qt.AlignRight) - body_layout.addWidget(footer_widget) + content_layout.addStretch(1) + + ok_btn = QtWidgets.QPushButton("OK", self) + + footer_layout = QtWidgets.QHBoxLayout() + footer_layout.addStretch(1) + footer_layout.addWidget(ok_btn, 0) + + bottom_line = self._create_line() + body_layout = QtWidgets.QVBoxLayout(self) + body_layout.addWidget(main_label_widget, 0) + body_layout.addWidget(content_scroll, 1) + body_layout.addWidget(bottom_line, 0) + body_layout.addLayout(footer_layout, 0) + + ok_btn.clicked.connect(self._on_ok_clicked) + + self.resize(660, 350) - def _on_accept(self): + def _on_ok_clicked(self): self.close() def _create_line(self): From 631ba5b12e35f669113b55dcd8d2a45136f42088 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 5 Jan 2022 17:50:50 +0100 Subject: [PATCH 363/492] line has different color --- openpype/tools/loader/widgets.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/tools/loader/widgets.py b/openpype/tools/loader/widgets.py index 80c4bade082..0e392aef865 100644 --- a/openpype/tools/loader/widgets.py +++ b/openpype/tools/loader/widgets.py @@ -188,9 +188,9 @@ def _on_ok_clicked(self): def _create_line(self): line = QtWidgets.QFrame(self) - line.setFixedHeight(2) - line.setFrameShape(QtWidgets.QFrame.HLine) - line.setFrameShadow(QtWidgets.QFrame.Sunken) + line.setObjectName("Separator") + line.setMinimumHeight(2) + line.setMaximumHeight(2) return line From 049d0d27f9152c3b690a6f5546ec02547bb89faf Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 5 Jan 2022 17:51:00 +0100 Subject: [PATCH 364/492] added ability to copy report --- openpype/tools/loader/widgets.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/openpype/tools/loader/widgets.py b/openpype/tools/loader/widgets.py index 0e392aef865..a39ac7213a7 100644 --- a/openpype/tools/loader/widgets.py +++ b/openpype/tools/loader/widgets.py @@ -142,7 +142,22 @@ def __init__(self, messages, parent=None): content_layout = QtWidgets.QVBoxLayout(content_widget) content_layout.setContentsMargins(0, 0, 0, 0) + report_data = [] for exc_msg, tb_text, repre, subset, version in messages: + report_message = ( + "During load error happened on Subset: \"{subset}\"" + " Representation: \"{repre}\" Version: {version}" + "\n\nError message: {message}" + ).format( + subset=subset, + repre=repre, + version=version, + message=exc_msg + ) + if tb_text: + report_message += "\n\n{}".format(tb_text) + report_data.append(report_message) + line = self._create_line() content_layout.addWidget(line) @@ -166,9 +181,11 @@ def __init__(self, messages, parent=None): content_layout.addStretch(1) + copy_report_btn = QtWidgets.QPushButton("Copy report", self) ok_btn = QtWidgets.QPushButton("OK", self) footer_layout = QtWidgets.QHBoxLayout() + footer_layout.addWidget(copy_report_btn, 0) footer_layout.addStretch(1) footer_layout.addWidget(ok_btn, 0) @@ -179,13 +196,25 @@ def __init__(self, messages, parent=None): body_layout.addWidget(bottom_line, 0) body_layout.addLayout(footer_layout, 0) + copy_report_btn.clicked.connect(self._on_copy_report) ok_btn.clicked.connect(self._on_ok_clicked) self.resize(660, 350) + self._report_data = report_data + def _on_ok_clicked(self): self.close() + def _on_copy_report(self): + report_text = (10 * "*").join(self._report_data) + + mime_data = QtCore.QMimeData() + mime_data.setText(report_text) + QtWidgets.QApplication.instance().clipboard().setMimeData( + mime_data + ) + def _create_line(self): line = QtWidgets.QFrame(self) line.setObjectName("Separator") From 723bfb2b3cc879c09f3ccdc2b89316e6ebfeb4c0 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 5 Jan 2022 18:04:06 +0100 Subject: [PATCH 365/492] flame: adding openpype marker on segment handling --- openpype/hosts/flame/api/lib.py | 152 +++++++++++++++++++++++++++++++- 1 file changed, 151 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index 44043c00f29..f91f593eb56 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -1,5 +1,6 @@ import sys import os +import json import pickle import contextlib from pprint import pformat @@ -8,6 +9,12 @@ log = Logger().get_logger(__name__) +class ctx: + # OpenPype marker workflow variables + marker_name = "OpenPypeData" + marker_duration = 0 + marker_color = (0.0, 1.0, 1.0) + publish_default = False @contextlib.contextmanager def io_preferences_file(klass, filepath, write=False): @@ -114,7 +121,7 @@ def __init__(self): self.hostname, ) - self.log.info("[{}] waking up".format(self.__class__.__name__)) + self.log.info("[{}] waking up".format(self.__class__.__name__)) try: self.load_prefs() @@ -337,3 +344,146 @@ def process(self, project_name): policy_wiretap = GetProjectColorPolicy(_log=_log) return policy_wiretap.process(project_name) + + +def get_segment_pype_tag(segment, with_marker=None): + """ + Get openpype track item tag created by creator or loader plugin. + + Attributes: + segment (flame.PySegment): flame api object + with_marker (bool)[optional]: if true it will return also marker object + + Returns: + dict: openpype tag data + + Returns(with_marker=True): + flame.PyMarker, dict + """ + for marker in segment.markers: + comment = marker.comment.get_value() + color = marker.colour.get_value() + name = marker.name.get_value() + + if name == ctx.marker_name and color == ctx.marker_color: + if not with_marker: + return json.loads(comment) + else: + return marker, json.loads(comment) + + +def set_segment_pype_tag(segment, data=None): + """ + Set openpype track item tag to input segment. + + Attributes: + segment (flame.PySegment): flame api object + + Returns: + dict: json loaded data + """ + data = data or dict() + + marker_data = get_segment_pype_tag(segment, True) + + if marker_data: + # get available openpype tag if any + marker, tag_data = marker_data + # update tag data with new data + tag_data.update(data) + # update marker with tag data + marker.comment = json.dumps(tag_data) + + return True + else: + # update tag data with new data + marker = create_pype_marker(segment) + # add tag data to marker's comment + marker.comment = json.dumps(data) + + return True + + + +def imprint(segment, data=None): + """ + Adding openpype data to Flame timeline segment. + + Also including publish attribute into tag. + + Arguments: + segment (flame.PySegment)): flame api object + data (dict): Any data which needst to be imprinted + + Examples: + data = { + 'asset': 'sq020sh0280', + 'family': 'render', + 'subset': 'subsetMain' + } + """ + data = data or {} + + if not set_segment_pype_tag(segment, data): + raise AttributeError("Not imprint data to segment") + + # add publish attribute + set_publish_attribute(segment, True) + + +def set_publish_attribute(segment, value): + """ Set Publish attribute in input Tag object + + Attribute: + segment (flame.PySegment)): flame api object + value (bool): True or False + """ + tag_data = get_segment_pype_tag(segment) + tag_data["publish"] = value + + # set data to the publish attribute + if not set_segment_pype_tag(segment, tag_data): + raise AttributeError("Not imprint data to segment") + + +def get_publish_attribute(segment): + """ Get Publish attribute from input Tag object + + Attribute: + segment (flame.PySegment)): flame api object + + Returns: + bool: True or False + """ + tag_data = get_segment_pype_tag(segment) + + if not tag_data: + set_publish_attribute(segment, ctx.publish_default) + return ctx.publish_default + + return tag_data["publish"] + + +def create_pype_marker(segment): + """ Create openpype marker on a segment. + + Attributes: + segment (flame.PySegment): flame api object + + Returns: + flame.PyMarker: flame api object + """ + # get duration of segment + duration = segment.record_duration.relative_frame + # calculate start frame of the new marker + start_frame = int(segment.record_in.relative_frame) + int(duration / 2) + # create marker + marker = segment.create_marker(start_frame) + # set marker name + marker.name = ctx.marker_name + # set duration + marker.duration = ctx.marker_duration + # set colour + marker.colour = ctx.marker_color + + return marker \ No newline at end of file From 7d72481b865fcdb78e8cd26c924f37a997533423 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 5 Jan 2022 18:31:08 +0100 Subject: [PATCH 366/492] create base of error dialog in utils --- openpype/tools/loader/widgets.py | 159 ++++++--------------------- openpype/tools/utils/__init__.py | 6 +- openpype/tools/utils/error_dialog.py | 143 ++++++++++++++++++++++++ 3 files changed, 180 insertions(+), 128 deletions(-) create mode 100644 openpype/tools/utils/error_dialog.py diff --git a/openpype/tools/loader/widgets.py b/openpype/tools/loader/widgets.py index a39ac7213a7..3accaed5ab9 100644 --- a/openpype/tools/loader/widgets.py +++ b/openpype/tools/loader/widgets.py @@ -11,15 +11,16 @@ from avalon import api, pipeline from avalon.lib import HeroVersionType -from openpype.tools.utils import lib as tools_lib +from openpype.tools.utils import ( + ErrorMessageBox, + lib as tools_lib +) from openpype.tools.utils.delegates import ( VersionDelegate, PrettyTimeDelegate ) from openpype.tools.utils.widgets import ( OptionalMenu, - ClickableFrame, - ExpandBtn, PlaceholderLineEdit ) from openpype.tools.utils.views import ( @@ -66,66 +67,12 @@ def set_label(self, label): self.label_widget.setText(label) -class TracebackWidget(QtWidgets.QWidget): - def __init__(self, tb_text, parent): - super(TracebackWidget, self).__init__(parent) - - # Modify text to match html - # - add more replacements when needed - tb_text = ( - tb_text - .replace("<", "<") - .replace(">", ">") - .replace("\n", "
") - .replace(" ", " ") - ) - - expand_btn = ExpandBtn(self) - - clickable_frame = ClickableFrame(self) - clickable_layout = QtWidgets.QHBoxLayout(clickable_frame) - clickable_layout.setContentsMargins(0, 0, 0, 0) - - expand_label = QtWidgets.QLabel("Details", clickable_frame) - clickable_layout.addWidget(expand_label, 0) - clickable_layout.addStretch(1) - - show_details_layout = QtWidgets.QHBoxLayout() - show_details_layout.addWidget(expand_btn, 0) - show_details_layout.addWidget(clickable_frame, 1) - - text_widget = QtWidgets.QLabel(self) - text_widget.setText(tb_text) - text_widget.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) - text_widget.setVisible(False) - - layout = QtWidgets.QVBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.addLayout(show_details_layout, 0) - layout.addWidget(text_widget, 1) - - clickable_frame.clicked.connect(self._on_show_details_click) - expand_btn.clicked.connect(self._on_show_details_click) - - self._expand_btn = expand_btn - self._text_widget = text_widget - - def _on_show_details_click(self): - self._text_widget.setVisible(not self._text_widget.isVisible()) - self._expand_btn.set_collapsed(not self._text_widget.isVisible()) - - -class LoadErrorMessageBox(QtWidgets.QDialog): +class LoadErrorMessageBox(ErrorMessageBox): def __init__(self, messages, parent=None): - super(LoadErrorMessageBox, self).__init__(parent) - self.setWindowTitle("Loading failed") - self.setFocusPolicy(QtCore.Qt.StrongFocus) - - main_label = ( - "Failed to load items" - ) - main_label_widget = QtWidgets.QLabel(main_label, self) + self._messages = messages + super(LoadErrorMessageBox, self).__init__("Loading failed", parent) + def _create_content(self, content_layout): item_name_template = ( "Subset: {}
" "Version: {}
" @@ -133,31 +80,7 @@ def __init__(self, messages, parent=None): ) exc_msg_template = "{}" - content_scroll = QtWidgets.QScrollArea(self) - content_scroll.setWidgetResizable(True) - - content_widget = QtWidgets.QWidget(content_scroll) - content_scroll.setWidget(content_widget) - - content_layout = QtWidgets.QVBoxLayout(content_widget) - content_layout.setContentsMargins(0, 0, 0, 0) - - report_data = [] - for exc_msg, tb_text, repre, subset, version in messages: - report_message = ( - "During load error happened on Subset: \"{subset}\"" - " Representation: \"{repre}\" Version: {version}" - "\n\nError message: {message}" - ).format( - subset=subset, - repre=repre, - version=version, - message=exc_msg - ) - if tb_text: - report_message += "\n\n{}".format(tb_text) - report_data.append(report_message) - + for exc_msg, tb_text, repre, subset, version in self._messages: line = self._create_line() content_layout.addWidget(line) @@ -175,52 +98,34 @@ def __init__(self, messages, parent=None): if tb_text: line = self._create_line() - tb_widget = TracebackWidget(tb_text, self) + tb_widget = self._create_traceback_widget(tb_text, self) content_layout.addWidget(line) content_layout.addWidget(tb_widget) - content_layout.addStretch(1) - - copy_report_btn = QtWidgets.QPushButton("Copy report", self) - ok_btn = QtWidgets.QPushButton("OK", self) - - footer_layout = QtWidgets.QHBoxLayout() - footer_layout.addWidget(copy_report_btn, 0) - footer_layout.addStretch(1) - footer_layout.addWidget(ok_btn, 0) - - bottom_line = self._create_line() - body_layout = QtWidgets.QVBoxLayout(self) - body_layout.addWidget(main_label_widget, 0) - body_layout.addWidget(content_scroll, 1) - body_layout.addWidget(bottom_line, 0) - body_layout.addLayout(footer_layout, 0) - - copy_report_btn.clicked.connect(self._on_copy_report) - ok_btn.clicked.connect(self._on_ok_clicked) - - self.resize(660, 350) - - self._report_data = report_data - - def _on_ok_clicked(self): - self.close() - - def _on_copy_report(self): - report_text = (10 * "*").join(self._report_data) + def _get_report_data(self): + report_data = [] + for exc_msg, tb_text, repre, subset, version in self._messages: + report_message = ( + "During load error happened on Subset: \"{subset}\"" + " Representation: \"{repre}\" Version: {version}" + "\n\nError message: {message}" + ).format( + subset=subset, + repre=repre, + version=version, + message=exc_msg + ) + if tb_text: + report_message += "\n\n{}".format(tb_text) + report_data.append(report_message) + return report_data - mime_data = QtCore.QMimeData() - mime_data.setText(report_text) - QtWidgets.QApplication.instance().clipboard().setMimeData( - mime_data + def _create_top_widget(self, parent_widget): + label_widget = QtWidgets.QLabel(parent_widget) + label_widget.setText( + "Failed to load items" ) - - def _create_line(self): - line = QtWidgets.QFrame(self) - line.setObjectName("Separator") - line.setMinimumHeight(2) - line.setMaximumHeight(2) - return line + return label_widget class SubsetWidget(QtWidgets.QWidget): diff --git a/openpype/tools/utils/__init__.py b/openpype/tools/utils/__init__.py index 294b919b5c8..4dd6bdd05f2 100644 --- a/openpype/tools/utils/__init__.py +++ b/openpype/tools/utils/__init__.py @@ -5,10 +5,14 @@ ExpandBtn, ) +from .error_dialog import ErrorMessageBox + __all__ = ( "PlaceholderLineEdit", "BaseClickableFrame", "ClickableFrame", - "ExpandBtn" + "ExpandBtn", + + "ErrorMessageBox" ) diff --git a/openpype/tools/utils/error_dialog.py b/openpype/tools/utils/error_dialog.py new file mode 100644 index 00000000000..2f39ccf139d --- /dev/null +++ b/openpype/tools/utils/error_dialog.py @@ -0,0 +1,143 @@ +from Qt import QtWidgets, QtCore + +from .widgets import ClickableFrame, ExpandBtn + + +class TracebackWidget(QtWidgets.QWidget): + def __init__(self, tb_text, parent): + super(TracebackWidget, self).__init__(parent) + + # Modify text to match html + # - add more replacements when needed + tb_text = ( + tb_text + .replace("<", "<") + .replace(">", ">") + .replace("\n", "
") + .replace(" ", " ") + ) + + expand_btn = ExpandBtn(self) + + clickable_frame = ClickableFrame(self) + clickable_layout = QtWidgets.QHBoxLayout(clickable_frame) + clickable_layout.setContentsMargins(0, 0, 0, 0) + + expand_label = QtWidgets.QLabel("Details", clickable_frame) + clickable_layout.addWidget(expand_label, 0) + clickable_layout.addStretch(1) + + show_details_layout = QtWidgets.QHBoxLayout() + show_details_layout.addWidget(expand_btn, 0) + show_details_layout.addWidget(clickable_frame, 1) + + text_widget = QtWidgets.QLabel(self) + text_widget.setText(tb_text) + text_widget.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) + text_widget.setVisible(False) + + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.addLayout(show_details_layout, 0) + layout.addWidget(text_widget, 1) + + clickable_frame.clicked.connect(self._on_show_details_click) + expand_btn.clicked.connect(self._on_show_details_click) + + self._expand_btn = expand_btn + self._text_widget = text_widget + + def _on_show_details_click(self): + self._text_widget.setVisible(not self._text_widget.isVisible()) + self._expand_btn.set_collapsed(not self._text_widget.isVisible()) + + +class ErrorMessageBox(QtWidgets.QDialog): + _default_width = 660 + _default_height = 350 + + def __init__(self, title, parent): + super(ErrorMessageBox, self).__init__(parent) + self.setWindowTitle(title) + self.setFocusPolicy(QtCore.Qt.StrongFocus) + + top_widget = self._create_top_widget(self) + + content_scroll = QtWidgets.QScrollArea(self) + content_scroll.setWidgetResizable(True) + + content_widget = QtWidgets.QWidget(content_scroll) + content_scroll.setWidget(content_widget) + + content_layout = QtWidgets.QVBoxLayout(content_widget) + content_layout.setContentsMargins(0, 0, 0, 0) + + self._create_content(content_layout) + + content_layout.addStretch(1) + + copy_report_btn = QtWidgets.QPushButton("Copy report", self) + ok_btn = QtWidgets.QPushButton("OK", self) + + footer_layout = QtWidgets.QHBoxLayout() + footer_layout.addWidget(copy_report_btn, 0) + footer_layout.addStretch(1) + footer_layout.addWidget(ok_btn, 0) + + bottom_line = self._create_line() + body_layout = QtWidgets.QVBoxLayout(self) + body_layout.addWidget(top_widget, 0) + body_layout.addWidget(content_scroll, 1) + body_layout.addWidget(bottom_line, 0) + body_layout.addLayout(footer_layout, 0) + + copy_report_btn.clicked.connect(self._on_copy_report) + ok_btn.clicked.connect(self._on_ok_clicked) + + self.resize(self._default_width, self._default_height) + + report_data = self._get_report_data() + if not report_data: + copy_report_btn.setVisible(False) + + self._report_data = report_data + self._content_widget = content_widget + + def _create_top_widget(self, parent_widget): + label_widget = QtWidgets.QLabel(parent_widget) + label_widget.setText( + "Something went wrong" + ) + return label_widget + + def _create_content(self, content_layout): + raise NotImplementedError( + "Method '_fill_content_layout' is not implemented!" + ) + + def _get_report_data(self): + return [] + + def _on_ok_clicked(self): + self.close() + + def _on_copy_report(self): + report_text = (10 * "*").join(self._report_data) + + mime_data = QtCore.QMimeData() + mime_data.setText(report_text) + QtWidgets.QApplication.instance().clipboard().setMimeData( + mime_data + ) + + def _create_line(self): + line = QtWidgets.QFrame(self) + line.setObjectName("Separator") + line.setMinimumHeight(2) + line.setMaximumHeight(2) + return line + + def _create_traceback_widget(self, traceback_text, parent=None): + if parent is None: + parent = self._content_widget + return TracebackWidget(traceback_text, parent) From 7107e797f0f0dfb0b5106062bcc3060cf3424de7 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 5 Jan 2022 18:42:18 +0100 Subject: [PATCH 367/492] extracted function for preparation of text with html symbols --- openpype/tools/loader/widgets.py | 50 ++++++++++++++-------------- openpype/tools/utils/error_dialog.py | 23 ++++++++----- 2 files changed, 40 insertions(+), 33 deletions(-) diff --git a/openpype/tools/loader/widgets.py b/openpype/tools/loader/widgets.py index 3accaed5ab9..ed130f765c7 100644 --- a/openpype/tools/loader/widgets.py +++ b/openpype/tools/loader/widgets.py @@ -72,6 +72,31 @@ def __init__(self, messages, parent=None): self._messages = messages super(LoadErrorMessageBox, self).__init__("Loading failed", parent) + def _create_top_widget(self, parent_widget): + label_widget = QtWidgets.QLabel(parent_widget) + label_widget.setText( + "Failed to load items" + ) + return label_widget + + def _get_report_data(self): + report_data = [] + for exc_msg, tb_text, repre, subset, version in self._messages: + report_message = ( + "During load error happened on Subset: \"{subset}\"" + " Representation: \"{repre}\" Version: {version}" + "\n\nError message: {message}" + ).format( + subset=subset, + repre=repre, + version=version, + message=exc_msg + ) + if tb_text: + report_message += "\n\n{}".format(tb_text) + report_data.append(report_message) + return report_data + def _create_content(self, content_layout): item_name_template = ( "Subset: {}
" @@ -102,31 +127,6 @@ def _create_content(self, content_layout): content_layout.addWidget(line) content_layout.addWidget(tb_widget) - def _get_report_data(self): - report_data = [] - for exc_msg, tb_text, repre, subset, version in self._messages: - report_message = ( - "During load error happened on Subset: \"{subset}\"" - " Representation: \"{repre}\" Version: {version}" - "\n\nError message: {message}" - ).format( - subset=subset, - repre=repre, - version=version, - message=exc_msg - ) - if tb_text: - report_message += "\n\n{}".format(tb_text) - report_data.append(report_message) - return report_data - - def _create_top_widget(self, parent_widget): - label_widget = QtWidgets.QLabel(parent_widget) - label_widget.setText( - "Failed to load items" - ) - return label_widget - class SubsetWidget(QtWidgets.QWidget): """A widget that lists the published subsets for an asset""" diff --git a/openpype/tools/utils/error_dialog.py b/openpype/tools/utils/error_dialog.py index 2f39ccf139d..0336f4bb088 100644 --- a/openpype/tools/utils/error_dialog.py +++ b/openpype/tools/utils/error_dialog.py @@ -3,20 +3,23 @@ from .widgets import ClickableFrame, ExpandBtn +def convert_text_for_html(text): + return ( + text + .replace("<", "<") + .replace(">", ">") + .replace("\n", "
") + .replace(" ", " ") + ) + + class TracebackWidget(QtWidgets.QWidget): def __init__(self, tb_text, parent): super(TracebackWidget, self).__init__(parent) # Modify text to match html # - add more replacements when needed - tb_text = ( - tb_text - .replace("<", "<") - .replace(">", ">") - .replace("\n", "
") - .replace(" ", " ") - ) - + tb_text = convert_text_for_html(tb_text) expand_btn = ExpandBtn(self) clickable_frame = ClickableFrame(self) @@ -103,6 +106,10 @@ def __init__(self, title, parent): self._report_data = report_data self._content_widget = content_widget + @staticmethod + def convert_text_for_html(text): + return convert_text_for_html(text) + def _create_top_widget(self, parent_widget): label_widget = QtWidgets.QLabel(parent_widget) label_widget.setText( From acca8849a273daefe4b58567060edc4433ca96d2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 5 Jan 2022 18:43:01 +0100 Subject: [PATCH 368/492] pass parent to creator error dialog --- openpype/tools/creator/window.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openpype/tools/creator/window.py b/openpype/tools/creator/window.py index dca1735121a..22a6d5ce9c1 100644 --- a/openpype/tools/creator/window.py +++ b/openpype/tools/creator/window.py @@ -445,7 +445,11 @@ def _on_create(self): if error_info: box = CreateErrorMessageBox( - creator_plugin.family, subset_name, asset_name, *error_info + creator_plugin.family, + subset_name, + asset_name, + *error_info, + parent=self ) box.show() # Store dialog so is not garbage collected before is shown From 6cc944d46ca0cded5f6324f680fe1a4835279aef Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 5 Jan 2022 18:43:15 +0100 Subject: [PATCH 369/492] use ErrorMessageBox in creator too --- openpype/tools/creator/widgets.py | 103 ++++++++++++++---------------- 1 file changed, 49 insertions(+), 54 deletions(-) diff --git a/openpype/tools/creator/widgets.py b/openpype/tools/creator/widgets.py index 89c90cc048c..d2258a31c54 100644 --- a/openpype/tools/creator/widgets.py +++ b/openpype/tools/creator/widgets.py @@ -7,9 +7,10 @@ from openpype import style from openpype.pipeline.create import SUBSET_NAME_ALLOWED_SYMBOLS +from openpype.tools.utils import ErrorMessageBox -class CreateErrorMessageBox(QtWidgets.QDialog): +class CreateErrorMessageBox(ErrorMessageBox): def __init__( self, family, @@ -17,23 +18,38 @@ def __init__( asset_name, exc_msg, formatted_traceback, - parent=None + parent ): - super(CreateErrorMessageBox, self).__init__(parent) - self.setWindowTitle("Creation failed") - self.setFocusPolicy(QtCore.Qt.StrongFocus) - self.setWindowFlags( - self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint - ) - - body_layout = QtWidgets.QVBoxLayout(self) - - main_label = ( + self._family = family + self._subset_name = subset_name + self._asset_name = asset_name + self._exc_msg = exc_msg + self._formatted_traceback = formatted_traceback + super(CreateErrorMessageBox, self).__init__("Creation failed", parent) + + def _create_top_widget(self, parent_widget): + label_widget = QtWidgets.QLabel(parent_widget) + label_widget.setText( "Failed to create" ) - main_label_widget = QtWidgets.QLabel(main_label, self) - body_layout.addWidget(main_label_widget) + return label_widget + + def _get_report_data(self): + report_message = ( + "Failed to create Subset: \"{subset}\" Family: \"{family}\"" + " in Asset: \"{asset}\"" + "\n\nError: {message}" + ).format( + subset=self._subset_name, + family=self._family, + asset=self._asset, + message=self._exc_msg + ) + if self._formatted_traceback: + report_message += "\n\n{}".format(self._formatted_traceback) + return [report_message] + def _create_content(self, content_layout): item_name_template = ( "Family: {}
" "Subset: {}
" @@ -42,50 +58,29 @@ def __init__( exc_msg_template = "{}" line = self._create_line() - body_layout.addWidget(line) - - item_name = item_name_template.format(family, subset_name, asset_name) - item_name_widget = QtWidgets.QLabel( - item_name.replace("\n", "
"), self + content_layout.addWidget(line) + + item_name_widget = QtWidgets.QLabel(self) + item_name_widget.setText( + self.convert_text_for_html( + item_name_template.format( + self._family, self._subset_name, self._asset_name + ) + ) ) - body_layout.addWidget(item_name_widget) + content_layout.addWidget(item_name_widget) - exc_msg = exc_msg_template.format(exc_msg.replace("\n", "
")) - message_label_widget = QtWidgets.QLabel(exc_msg, self) - body_layout.addWidget(message_label_widget) + message_label_widget = QtWidgets.QLabel(self) + message_label_widget.setText( + exc_msg_template.format(self.convert_text_for_html(self._exc_msg)) + ) + content_layout.addWidget(message_label_widget) - if formatted_traceback: - tb_widget = QtWidgets.QLabel( - formatted_traceback.replace("\n", "
"), self - ) - tb_widget.setTextInteractionFlags( - QtCore.Qt.TextBrowserInteraction + if self._formatted_traceback: + tb_widget = self._create_traceback_widget( + self._formatted_traceback ) - body_layout.addWidget(tb_widget) - - footer_widget = QtWidgets.QWidget(self) - footer_layout = QtWidgets.QHBoxLayout(footer_widget) - button_box = QtWidgets.QDialogButtonBox(QtCore.Qt.Vertical) - button_box.setStandardButtons( - QtWidgets.QDialogButtonBox.StandardButton.Ok - ) - button_box.accepted.connect(self._on_accept) - footer_layout.addWidget(button_box, alignment=QtCore.Qt.AlignRight) - body_layout.addWidget(footer_widget) - - def showEvent(self, event): - self.setStyleSheet(style.load_stylesheet()) - super(CreateErrorMessageBox, self).showEvent(event) - - def _on_accept(self): - self.close() - - def _create_line(self): - line = QtWidgets.QFrame(self) - line.setFixedHeight(2) - line.setFrameShape(QtWidgets.QFrame.HLine) - line.setFrameShadow(QtWidgets.QFrame.Sunken) - return line + content_layout.addWidget(tb_widget) class SubsetNameValidator(QtGui.QRegExpValidator): From e410addc46159f8f0c547d761890173548866dc5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 5 Jan 2022 18:50:09 +0100 Subject: [PATCH 370/492] minor fixes of dialog --- openpype/tools/creator/widgets.py | 10 +++++----- openpype/tools/utils/error_dialog.py | 4 +++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/openpype/tools/creator/widgets.py b/openpype/tools/creator/widgets.py index d2258a31c54..9dd435c1ccf 100644 --- a/openpype/tools/creator/widgets.py +++ b/openpype/tools/creator/widgets.py @@ -42,7 +42,7 @@ def _get_report_data(self): ).format( subset=self._subset_name, family=self._family, - asset=self._asset, + asset=self._asset_name, message=self._exc_msg ) if self._formatted_traceback: @@ -62,10 +62,8 @@ def _create_content(self, content_layout): item_name_widget = QtWidgets.QLabel(self) item_name_widget.setText( - self.convert_text_for_html( - item_name_template.format( - self._family, self._subset_name, self._asset_name - ) + item_name_template.format( + self._family, self._subset_name, self._asset_name ) ) content_layout.addWidget(item_name_widget) @@ -77,9 +75,11 @@ def _create_content(self, content_layout): content_layout.addWidget(message_label_widget) if self._formatted_traceback: + line_widget = self._create_line() tb_widget = self._create_traceback_widget( self._formatted_traceback ) + content_layout.addWidget(line_widget) content_layout.addWidget(tb_widget) diff --git a/openpype/tools/utils/error_dialog.py b/openpype/tools/utils/error_dialog.py index 0336f4bb088..f7b12bb69f2 100644 --- a/openpype/tools/utils/error_dialog.py +++ b/openpype/tools/utils/error_dialog.py @@ -75,6 +75,9 @@ def __init__(self, title, parent): content_layout = QtWidgets.QVBoxLayout(content_widget) content_layout.setContentsMargins(0, 0, 0, 0) + # Store content widget before creation of content + self._content_widget = content_widget + self._create_content(content_layout) content_layout.addStretch(1) @@ -104,7 +107,6 @@ def __init__(self, title, parent): copy_report_btn.setVisible(False) self._report_data = report_data - self._content_widget = content_widget @staticmethod def convert_text_for_html(text): From 9d0e3363c5bfab9741ee2be01ff3e5f56d5b59cf Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 5 Jan 2022 21:44:29 +0100 Subject: [PATCH 371/492] flame: testing in publish plugin --- .../plugins/publish/collect_test_selection.py | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/openpype/hosts/flame/plugins/publish/collect_test_selection.py b/openpype/hosts/flame/plugins/publish/collect_test_selection.py index 9a80a924140..d30d6ed331c 100644 --- a/openpype/hosts/flame/plugins/publish/collect_test_selection.py +++ b/openpype/hosts/flame/plugins/publish/collect_test_selection.py @@ -24,3 +24,24 @@ def process(self, context): otio_timeline = otio_export.create_otio_timeline(sequence) self.log.info(pformat(otio_timeline)) + + # test segment markers + for ver in sequence.versions: + for track in ver.tracks: + if len(track.segments) == 0 and track.hidden: + continue + + for segment in track.segments: + if str(segment.name)[1:-1] == "": + continue + if not segment.selected: + continue + + self.log.debug("Segment with OpenPypeData: {}".format( + segment.name)) + + lib.imprint(segment, { + 'asset': 'sq020sh0280', + 'family': 'render', + 'subset': 'subsetMain' + }) From 5d39784abb1228bddbb5abf0fc329c5d3d4aecfc Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 5 Jan 2022 22:32:07 +0100 Subject: [PATCH 372/492] flame: maintained segment selection --- openpype/hosts/flame/api/lib.py | 45 +++++++++++++++++++++++++--- openpype/hosts/flame/api/pipeline.py | 26 ---------------- 2 files changed, 41 insertions(+), 30 deletions(-) diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index f91f593eb56..5860bb728df 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -393,15 +393,13 @@ def set_segment_pype_tag(segment, data=None): tag_data.update(data) # update marker with tag data marker.comment = json.dumps(tag_data) - - return True else: # update tag data with new data marker = create_pype_marker(segment) # add tag data to marker's comment marker.comment = json.dumps(data) - return True + return True @@ -486,4 +484,43 @@ def create_pype_marker(segment): # set colour marker.colour = ctx.marker_color - return marker \ No newline at end of file + return marker + + +@contextlib.contextmanager +def maintained_segment_selection(sequence): + """Maintain selection during context + + Example: + >>> with maintained_selection(): + ... node['selected'].setValue(True) + >>> print(node['selected'].value()) + False + """ + selected_segments = [] + for ver in sequence.versions: + for track in ver.tracks: + if len(track.segments) == 0 and track.hidden: + continue + for segment in track.segments: + if segment.selected != True: + continue + selected_segments.append(segment) + try: + # do the operation + yield + finally: + reset_segment_selection(sequence) + for segment in selected_segments: + segment.selected = True + + +def reset_segment_selection(sequence): + """Deselect all selected nodes + """ + for ver in sequence.versions: + for track in ver.tracks: + if len(track.segments) == 0 and track.hidden: + continue + for segment in track.segments: + segment.selected = False diff --git a/openpype/hosts/flame/api/pipeline.py b/openpype/hosts/flame/api/pipeline.py index 26dfe7c0320..00860857f1e 100644 --- a/openpype/hosts/flame/api/pipeline.py +++ b/openpype/hosts/flame/api/pipeline.py @@ -97,32 +97,6 @@ def update_container(tl_segment, data=None): # TODO: update_container pass - -@contextlib.contextmanager -def maintained_selection(): - """Maintain selection during context - - Example: - >>> with maintained_selection(): - ... node['selected'].setValue(True) - >>> print(node['selected'].value()) - False - """ - # TODO: maintained_selection + remove undo steps - - try: - # do the operation - yield - finally: - pass - - -def reset_selection(): - """Deselect all selected nodes - """ - pass - - def on_pyblish_instance_toggled(instance, old_value, new_value): """Toggle node passthrough states on instance toggles.""" From 655e85f12ad5c4e98b777c1f90e285fb838046a8 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 5 Jan 2022 22:46:37 +0100 Subject: [PATCH 373/492] flame: adding docstring to maintained segment selection --- openpype/hosts/flame/api/lib.py | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index 5860bb728df..0ba6d81c0d0 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -491,26 +491,41 @@ def create_pype_marker(segment): def maintained_segment_selection(sequence): """Maintain selection during context + Attributes: + sequence (flame.PySequence): python api object + + Yield: + list of flame.PySegment + Example: - >>> with maintained_selection(): - ... node['selected'].setValue(True) - >>> print(node['selected'].value()) - False + >>> with maintained_segment_selection(sequence) as selected_segments: + ... for segment in selected_segments: + ... segment.selected = False + >>> print(segment.selected) + True """ selected_segments = [] + # loop versions in sequence for ver in sequence.versions: + # loop track in versions for track in ver.tracks: + # ignore all empty tracks and hidden too if len(track.segments) == 0 and track.hidden: continue + # loop all segment in remaining tracks for segment in track.segments: + # ignore all segments not selected if segment.selected != True: continue + # add it to original selection selected_segments.append(segment) try: - # do the operation - yield + # do the operation on selected segments + yield selected_segments finally: + # reset all selected clips reset_segment_selection(sequence) + # select only original selection of segments for segment in selected_segments: segment.selected = True From 152810f09d8eb61f17f39535819bcc789625f113 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 6 Jan 2022 10:39:43 +0100 Subject: [PATCH 374/492] flame: moving and renaming api function --- openpype/hosts/flame/__init__.py | 15 +++++++++- openpype/hosts/flame/api/lib.py | 43 ++++++---------------------- openpype/hosts/flame/api/pipeline.py | 31 ++++++++++++++++++-- 3 files changed, 50 insertions(+), 39 deletions(-) diff --git a/openpype/hosts/flame/__init__.py b/openpype/hosts/flame/__init__.py index da281706798..d6019100cb5 100644 --- a/openpype/hosts/flame/__init__.py +++ b/openpype/hosts/flame/__init__.py @@ -8,7 +8,6 @@ ls, containerise, update_container, - maintained_selection, remove_instance, list_instances, imprint @@ -21,6 +20,13 @@ get_current_project, get_current_sequence, create_bin, + create_segment_data_marker, + get_segment_data_marker, + set_segment_data_marker, + set_publish_attribute, + get_publish_attribute, + maintained_segment_selection, + reset_segment_selection ) from .api.menu import ( @@ -90,6 +96,13 @@ "get_current_project", "get_current_sequence", "create_bin", + "create_segment_data_marker", + "get_segment_data_marker", + "set_segment_data_marker", + "set_publish_attribute", + "get_publish_attribute", + "maintained_segment_selection", + "reset_segment_selection", # menu "FlameMenuProjectConnect", diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index 0ba6d81c0d0..03b4c1f619b 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -346,7 +346,7 @@ def process(self, project_name): return policy_wiretap.process(project_name) -def get_segment_pype_tag(segment, with_marker=None): +def get_segment_data_marker(segment, with_marker=None): """ Get openpype track item tag created by creator or loader plugin. @@ -372,7 +372,7 @@ def get_segment_pype_tag(segment, with_marker=None): return marker, json.loads(comment) -def set_segment_pype_tag(segment, data=None): +def set_segment_data_marker(segment, data=None): """ Set openpype track item tag to input segment. @@ -384,7 +384,7 @@ def set_segment_pype_tag(segment, data=None): """ data = data or dict() - marker_data = get_segment_pype_tag(segment, True) + marker_data = get_segment_data_marker(segment, True) if marker_data: # get available openpype tag if any @@ -395,40 +395,13 @@ def set_segment_pype_tag(segment, data=None): marker.comment = json.dumps(tag_data) else: # update tag data with new data - marker = create_pype_marker(segment) + marker = create_segment_data_marker(segment) # add tag data to marker's comment marker.comment = json.dumps(data) return True - -def imprint(segment, data=None): - """ - Adding openpype data to Flame timeline segment. - - Also including publish attribute into tag. - - Arguments: - segment (flame.PySegment)): flame api object - data (dict): Any data which needst to be imprinted - - Examples: - data = { - 'asset': 'sq020sh0280', - 'family': 'render', - 'subset': 'subsetMain' - } - """ - data = data or {} - - if not set_segment_pype_tag(segment, data): - raise AttributeError("Not imprint data to segment") - - # add publish attribute - set_publish_attribute(segment, True) - - def set_publish_attribute(segment, value): """ Set Publish attribute in input Tag object @@ -436,11 +409,11 @@ def set_publish_attribute(segment, value): segment (flame.PySegment)): flame api object value (bool): True or False """ - tag_data = get_segment_pype_tag(segment) + tag_data = get_segment_data_marker(segment) tag_data["publish"] = value # set data to the publish attribute - if not set_segment_pype_tag(segment, tag_data): + if not set_segment_data_marker(segment, tag_data): raise AttributeError("Not imprint data to segment") @@ -453,7 +426,7 @@ def get_publish_attribute(segment): Returns: bool: True or False """ - tag_data = get_segment_pype_tag(segment) + tag_data = get_segment_data_marker(segment) if not tag_data: set_publish_attribute(segment, ctx.publish_default) @@ -462,7 +435,7 @@ def get_publish_attribute(segment): return tag_data["publish"] -def create_pype_marker(segment): +def create_segment_data_marker(segment): """ Create openpype marker on a segment. Attributes: diff --git a/openpype/hosts/flame/api/pipeline.py b/openpype/hosts/flame/api/pipeline.py index 00860857f1e..22955896270 100644 --- a/openpype/hosts/flame/api/pipeline.py +++ b/openpype/hosts/flame/api/pipeline.py @@ -5,6 +5,10 @@ from avalon import api as avalon from pyblish import api as pyblish from openpype.api import Logger +from .lib import ( + set_segment_data_marker, + set_publish_attribute +) AVALON_CONTAINERS = "AVALON_CONTAINERS" @@ -124,6 +128,27 @@ def list_instances(): pass -def imprint(item, data=None): - # TODO: imprint - pass +def imprint(segment, data=None): + """ + Adding openpype data to Flame timeline segment. + + Also including publish attribute into tag. + + Arguments: + segment (flame.PySegment)): flame api object + data (dict): Any data which needst to be imprinted + + Examples: + data = { + 'asset': 'sq020sh0280', + 'family': 'render', + 'subset': 'subsetMain' + } + """ + data = data or {} + + if not set_segment_data_marker(segment, data): + raise AttributeError("Not imprint data to segment") + + # add publish attribute + set_publish_attribute(segment, True) From 6f8700a5c5dd4535f8c18837949219aaadf224c9 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 6 Jan 2022 12:46:05 +0100 Subject: [PATCH 375/492] OP-2282 - changes custom widget with CreatorError to standardize --- .../plugins/create/create_render.py | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/openpype/hosts/aftereffects/plugins/create/create_render.py b/openpype/hosts/aftereffects/plugins/create/create_render.py index b796e9eaac0..c73a1a1fc1e 100644 --- a/openpype/hosts/aftereffects/plugins/create/create_render.py +++ b/openpype/hosts/aftereffects/plugins/create/create_render.py @@ -1,6 +1,7 @@ -import openpype.api -from Qt import QtWidgets from avalon import aftereffects +from avalon.api import CreatorError + +import openpype.api import logging @@ -27,14 +28,13 @@ def process(self): folders=False, footages=False) if len(items) > 1: - self._show_msg("Please select only single composition at time.") - return False + raise CreatorError("Please select only single " + "composition at time.") if not items: - self._show_msg("Nothing to create. Select composition " + - "if 'useSelection' or create at least " + - "one composition.") - return False + raise CreatorError("Nothing to create. Select composition " + + "if 'useSelection' or create at least " + + "one composition.") existing_subsets = [instance['subset'].lower() for instance in aftereffects.list_instances()] @@ -42,8 +42,7 @@ def process(self): item = items.pop() if self.name.lower() in existing_subsets: txt = "Instance with name \"{}\" already exists.".format(self.name) - self._show_msg(txt) - return False + raise CreatorError(txt) self.data["members"] = [item.id] self.data["uuid"] = item.id # for SubsetManager @@ -54,9 +53,3 @@ def process(self): stub.imprint(item, self.data) stub.set_label_color(item.id, 14) # Cyan options 0 - 16 stub.rename_item(item.id, stub.PUBLISH_ICON + self.data["subset"]) - - def _show_msg(self, txt): - msg = QtWidgets.QMessageBox() - msg.setIcon(QtWidgets.QMessageBox.Warning) - msg.setText(txt) - msg.exec_() From 455a8a50b1d2ca98f33c6661c1e654886c53563a Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 6 Jan 2022 13:02:58 +0100 Subject: [PATCH 376/492] flame: add lib functionalities for segment operations --- openpype/hosts/flame/__init__.py | 6 +- openpype/hosts/flame/api/lib.py | 124 ++++++++++++++++++++++++++----- 2 files changed, 109 insertions(+), 21 deletions(-) diff --git a/openpype/hosts/flame/__init__.py b/openpype/hosts/flame/__init__.py index d6019100cb5..691b6f8119d 100644 --- a/openpype/hosts/flame/__init__.py +++ b/openpype/hosts/flame/__init__.py @@ -25,8 +25,10 @@ set_segment_data_marker, set_publish_attribute, get_publish_attribute, + get_sequence_segments, maintained_segment_selection, - reset_segment_selection + reset_segment_selection, + get_segment_attributes ) from .api.menu import ( @@ -101,8 +103,10 @@ "set_segment_data_marker", "set_publish_attribute", "get_publish_attribute", + "get_sequence_segments", "maintained_segment_selection", "reset_segment_selection", + "get_segment_attributes" # menu "FlameMenuProjectConnect", diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index 03b4c1f619b..2d30390d219 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -1,5 +1,6 @@ import sys import os +import re import json import pickle import contextlib @@ -13,8 +14,21 @@ class ctx: # OpenPype marker workflow variables marker_name = "OpenPypeData" marker_duration = 0 - marker_color = (0.0, 1.0, 1.0) + marker_color = "red" publish_default = False + color_map = { + "red": (1.0, 0.0, 0.0), + "orange": (1.0, 0.5, 0.0), + "yellow": (1.0, 1.0, 0.0), + "pink": (1.0, 0.5, 1.0), + "white": (1.0, 1.0, 1.0), + "green": (0.0, 1.0, 0.0), + "cyan": (0.0, 1.0, 1.0), + "blue": (0.0, 0.0, 1.0), + "purple": (0.5, 0.0, 0.5), + "magenta": (0.5, 0.0, 1.0), + "black": (0.0, 0.0, 0.0) +} @contextlib.contextmanager def io_preferences_file(klass, filepath, write=False): @@ -262,8 +276,8 @@ def get_media_storage(): def get_current_project(): - # TODO: get_current_project - return + import flame + return flame.project.current_project def get_current_sequence(selection): @@ -365,7 +379,7 @@ def get_segment_data_marker(segment, with_marker=None): color = marker.colour.get_value() name = marker.name.get_value() - if name == ctx.marker_name and color == ctx.marker_color: + if name == ctx.marker_name and color == ctx.color_map[ctx.marker_color]: if not with_marker: return json.loads(comment) else: @@ -455,10 +469,28 @@ def create_segment_data_marker(segment): # set duration marker.duration = ctx.marker_duration # set colour - marker.colour = ctx.marker_color + marker.colour = ctx.color_map[ctx.marker_color] # Red return marker +def get_sequence_segments(sequence, selected=False): + segments = [] + # loop versions in sequence + for ver in sequence.versions: + # loop track in versions + for track in ver.tracks: + # ignore all empty tracks and hidden too + if len(track.segments) == 0 and track.hidden: + continue + # loop all segment in remaining tracks + for segment in track.segments: + # ignore all segments not selected + if segment.selected != True and selected == True: + continue + # add it to original selection + segments.append(segment) + return segments + @contextlib.contextmanager def maintained_segment_selection(sequence): @@ -477,21 +509,7 @@ def maintained_segment_selection(sequence): >>> print(segment.selected) True """ - selected_segments = [] - # loop versions in sequence - for ver in sequence.versions: - # loop track in versions - for track in ver.tracks: - # ignore all empty tracks and hidden too - if len(track.segments) == 0 and track.hidden: - continue - # loop all segment in remaining tracks - for segment in track.segments: - # ignore all segments not selected - if segment.selected != True: - continue - # add it to original selection - selected_segments.append(segment) + selected_segments = get_sequence_segments(sequence, True) try: # do the operation on selected segments yield selected_segments @@ -512,3 +530,69 @@ def reset_segment_selection(sequence): continue for segment in track.segments: segment.selected = False + + +def _get_shot_tokens_values(clip, tokens): + old_value = None + output = {} + + if not clip.shot_name: + return output + + old_value = clip.shot_name.get_value() + + for token in tokens: + clip.shot_name.set_value(token) + _key = str(re.sub("[<>]", "", token)).replace(" ", "_") + + try: + output[_key] = int(clip.shot_name.get_value()) + except ValueError: + output[_key] = clip.shot_name.get_value() + + clip.shot_name.set_value(old_value) + + return output + + +def get_segment_attributes(segment): + if str(segment.name)[1:-1] == "": + return None + + # Add timeline segment to tree + clip_data = { + "segment_name": segment.name.get_value(), + "segment_comment": segment.comment.get_value(), + "tape_name": segment.tape_name, + "source_name": segment.source_name, + "fpath": segment.file_path, + "PySegment": segment + } + + # add all available shot tokens + shot_tokens = _get_shot_tokens_values(segment, [ + "", "", "", "", "", + "", "" + ]) + clip_data.update(shot_tokens) + + # populate shot source metadata + segment_attrs = [ + "record_duration", "record_in", "record_out", + "source_duration", "source_in", "source_out" + ] + segment_attrs_data = {} + for attr in segment_attrs: + if not hasattr(segment, attr): + continue + _value = getattr(segment, attr) + segment_attrs_data[attr] = str(_value).replace("+", ":") + + if attr in ["record_in", "record_out"]: + clip_data[attr] = _value.relative_frame + else: + clip_data[attr] = _value.frame + + clip_data["segment_timecodes"] = segment_attrs_data + + return clip_data From 8f721d3360ca3a445d9dd6ea10ec9c2aac64249f Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 6 Jan 2022 13:46:21 +0100 Subject: [PATCH 377/492] flame: create plugin abstractions --- openpype/hosts/flame/api/plugin.py | 623 +++++++++++++++++++++++++++++ openpype/hosts/flame/api/style.css | 26 ++ 2 files changed, 649 insertions(+) create mode 100644 openpype/hosts/flame/api/style.css diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index 2a28a20a752..1a3880a19a5 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -1,3 +1,626 @@ +import re +import os +from Qt import QtWidgets, QtCore +import openpype.api as openpype +import openpype.hosts.flame as opflame +from . import lib +from copy import deepcopy + +log = openpype.Logger().get_logger(__name__) + # Creator plugin functions +def load_stylesheet(): + path = os.path.join(os.path.dirname(__file__), "style.css") + if not os.path.exists(path): + log.warning("Unable to load stylesheet, file not found in resources") + return "" + + with open(path, "r") as file_stream: + stylesheet = file_stream.read() + return stylesheet + + +class CreatorWidget(QtWidgets.QDialog): + + # output items + items = dict() + + def __init__(self, name, info, ui_inputs, parent=None): + super(CreatorWidget, self).__init__(parent) + + self.setObjectName(name) + + self.setWindowFlags( + QtCore.Qt.Window + | QtCore.Qt.CustomizeWindowHint + | QtCore.Qt.WindowTitleHint + | QtCore.Qt.WindowCloseButtonHint + | QtCore.Qt.WindowStaysOnTopHint + ) + self.setWindowTitle(name or "Pype Creator Input") + self.resize(500, 700) + + # Where inputs and labels are set + self.content_widget = [QtWidgets.QWidget(self)] + top_layout = QtWidgets.QFormLayout(self.content_widget[0]) + top_layout.setObjectName("ContentLayout") + top_layout.addWidget(Spacer(5, self)) + + # first add widget tag line + top_layout.addWidget(QtWidgets.QLabel(info)) + + # main dynamic layout + self.scroll_area = QtWidgets.QScrollArea(self, widgetResizable=True) + self.scroll_area.setVerticalScrollBarPolicy( + QtCore.Qt.ScrollBarAsNeeded) + self.scroll_area.setVerticalScrollBarPolicy( + QtCore.Qt.ScrollBarAlwaysOn) + self.scroll_area.setHorizontalScrollBarPolicy( + QtCore.Qt.ScrollBarAlwaysOff) + self.scroll_area.setWidgetResizable(True) + + self.content_widget.append(self.scroll_area) + + scroll_widget = QtWidgets.QWidget(self) + in_scroll_area = QtWidgets.QVBoxLayout(scroll_widget) + self.content_layout = [in_scroll_area] + + # add preset data into input widget layout + self.items = self.populate_widgets(ui_inputs) + self.scroll_area.setWidget(scroll_widget) + + # Confirmation buttons + btns_widget = QtWidgets.QWidget(self) + btns_layout = QtWidgets.QHBoxLayout(btns_widget) + + cancel_btn = QtWidgets.QPushButton("Cancel") + btns_layout.addWidget(cancel_btn) + + ok_btn = QtWidgets.QPushButton("Ok") + btns_layout.addWidget(ok_btn) + + # Main layout of the dialog + main_layout = QtWidgets.QVBoxLayout(self) + main_layout.setContentsMargins(10, 10, 10, 10) + main_layout.setSpacing(0) + + # adding content widget + for w in self.content_widget: + main_layout.addWidget(w) + + main_layout.addWidget(btns_widget) + + ok_btn.clicked.connect(self._on_ok_clicked) + cancel_btn.clicked.connect(self._on_cancel_clicked) + + stylesheet = load_stylesheet() + self.setStyleSheet(stylesheet) + + def _on_ok_clicked(self): + self.result = self.value(self.items) + self.close() + + def _on_cancel_clicked(self): + self.result = None + self.close() + + def value(self, data, new_data=None): + new_data = new_data or dict() + for k, v in data.items(): + new_data[k] = { + "target": None, + "value": None + } + if v["type"] == "dict": + new_data[k]["target"] = v["target"] + new_data[k]["value"] = self.value(v["value"]) + if v["type"] == "section": + new_data.pop(k) + new_data = self.value(v["value"], new_data) + elif getattr(v["value"], "currentText", None): + new_data[k]["target"] = v["target"] + new_data[k]["value"] = v["value"].currentText() + elif getattr(v["value"], "isChecked", None): + new_data[k]["target"] = v["target"] + new_data[k]["value"] = v["value"].isChecked() + elif getattr(v["value"], "value", None): + new_data[k]["target"] = v["target"] + new_data[k]["value"] = v["value"].value() + elif getattr(v["value"], "text", None): + new_data[k]["target"] = v["target"] + new_data[k]["value"] = v["value"].text() + + return new_data + + def camel_case_split(self, text): + matches = re.finditer( + '.+?(?:(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|$)', text) + return " ".join([str(m.group(0)).capitalize() for m in matches]) + + def create_row(self, layout, type, text, **kwargs): + # get type attribute from qwidgets + attr = getattr(QtWidgets, type) + + # convert label text to normal capitalized text with spaces + label_text = self.camel_case_split(text) + + # assign the new text to lable widget + label = QtWidgets.QLabel(label_text) + label.setObjectName("LineLabel") + + # create attribute name text strip of spaces + attr_name = text.replace(" ", "") + + # create attribute and assign default values + setattr( + self, + attr_name, + attr(parent=self)) + + # assign the created attribute to variable + item = getattr(self, attr_name) + for func, val in kwargs.items(): + if getattr(item, func): + func_attr = getattr(item, func) + func_attr(val) + + # add to layout + layout.addRow(label, item) + + return item + + def populate_widgets(self, data, content_layout=None): + """ + Populate widget from input dict. + + Each plugin has its own set of widget rows defined in dictionary + each row values should have following keys: `type`, `target`, + `label`, `order`, `value` and optionally also `toolTip`. + + Args: + data (dict): widget rows or organized groups defined + by types `dict` or `section` + content_layout (QtWidgets.QFormLayout)[optional]: used when nesting + + Returns: + dict: redefined data dict updated with created widgets + + """ + + content_layout = content_layout or self.content_layout[-1] + # fix order of process by defined order value + ordered_keys = list(data.keys()) + for k, v in data.items(): + try: + # try removing a key from index which should + # be filled with new + ordered_keys.pop(v["order"]) + except IndexError: + pass + # add key into correct order + ordered_keys.insert(v["order"], k) + + # process ordered + for k in ordered_keys: + v = data[k] + tool_tip = v.get("toolTip", "") + if v["type"] == "dict": + # adding spacer between sections + self.content_layout.append(QtWidgets.QWidget(self)) + content_layout.addWidget(self.content_layout[-1]) + self.content_layout[-1].setObjectName("sectionHeadline") + + headline = QtWidgets.QVBoxLayout(self.content_layout[-1]) + headline.addWidget(Spacer(20, self)) + headline.addWidget(QtWidgets.QLabel(v["label"])) + + # adding nested layout with label + self.content_layout.append(QtWidgets.QWidget(self)) + self.content_layout[-1].setObjectName("sectionContent") + + nested_content_layout = QtWidgets.QFormLayout( + self.content_layout[-1]) + nested_content_layout.setObjectName("NestedContentLayout") + content_layout.addWidget(self.content_layout[-1]) + + # add nested key as label + data[k]["value"] = self.populate_widgets( + v["value"], nested_content_layout) + + if v["type"] == "section": + # adding spacer between sections + self.content_layout.append(QtWidgets.QWidget(self)) + content_layout.addWidget(self.content_layout[-1]) + self.content_layout[-1].setObjectName("sectionHeadline") + + headline = QtWidgets.QVBoxLayout(self.content_layout[-1]) + headline.addWidget(Spacer(20, self)) + headline.addWidget(QtWidgets.QLabel(v["label"])) + + # adding nested layout with label + self.content_layout.append(QtWidgets.QWidget(self)) + self.content_layout[-1].setObjectName("sectionContent") + + nested_content_layout = QtWidgets.QFormLayout( + self.content_layout[-1]) + nested_content_layout.setObjectName("NestedContentLayout") + content_layout.addWidget(self.content_layout[-1]) + + # add nested key as label + data[k]["value"] = self.populate_widgets( + v["value"], nested_content_layout) + + elif v["type"] == "QLineEdit": + data[k]["value"] = self.create_row( + content_layout, "QLineEdit", v["label"], + setText=v["value"], setToolTip=tool_tip) + elif v["type"] == "QComboBox": + data[k]["value"] = self.create_row( + content_layout, "QComboBox", v["label"], + addItems=v["value"], setToolTip=tool_tip) + elif v["type"] == "QCheckBox": + data[k]["value"] = self.create_row( + content_layout, "QCheckBox", v["label"], + setChecked=v["value"], setToolTip=tool_tip) + elif v["type"] == "QSpinBox": + data[k]["value"] = self.create_row( + content_layout, "QSpinBox", v["label"], + setValue=v["value"], setMinimum=0, + setMaximum=100000, setToolTip=tool_tip) + return data + + +class Spacer(QtWidgets.QWidget): + def __init__(self, height, *args, **kwargs): + super(self.__class__, self).__init__(*args, **kwargs) + + self.setFixedHeight(height) + + real_spacer = QtWidgets.QWidget(self) + real_spacer.setObjectName("Spacer") + real_spacer.setFixedHeight(height) + + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.addWidget(real_spacer) + + self.setLayout(layout) + + +class Creator(openpype.Creator): + """Creator class wrapper + """ + clip_color = lib.ctx.color_map["purple"] + rename_index = None + + def __init__(self, *args, **kwargs): + super(Creator, self).__init__(*args, **kwargs) + self.presets = openpype.get_current_project_settings()[ + "flame"]["create"].get(self.__class__.__name__, {}) + + # adding basic current context flame objects + self.project = lib.get_current_project() + self.sequence = lib.get_current_sequence(opflame.selection) + + if (self.options or {}).get("useSelection"): + self.selected = lib.get_sequence_segments(self.sequence, True) + else: + self.selected = lib.get_sequence_segments(self.sequence) + + self.widget = CreatorWidget + + +class PublishableClip: + """ + Convert a segment to publishable instance + + Args: + segment (flame.PySegment): flame api object + kwargs (optional): additional data needed for rename=True (presets) + + Returns: + flame.PySegment: flame api object + """ + vertical_clip_match = dict() + tag_data = dict() + types = { + "shot": "shot", + "folder": "folder", + "episode": "episode", + "sequence": "sequence", + "track": "sequence", + } + + # parents search patern + parents_search_patern = r"\{([a-z]*?)\}" + + # default templates for non-ui use + rename_default = False + hierarchy_default = "{_folder_}/{_sequence_}/{_track_}" + clip_name_default = "shot_{_trackIndex_:0>3}_{_clipIndex_:0>4}" + subset_name_default = "[ track name ]" + review_track_default = "[ none ]" + subset_family_default = "plate" + count_from_default = 10 + count_steps_default = 10 + vertical_sync_default = False + driving_layer_default = "" + + def __init__(self, cls, segment, **kwargs): + # populate input cls attribute onto self.[attr] + self.__dict__.update(cls.__dict__) + + # get main parent objects + self.current_segment = segment + sequence_name = lib.get_current_sequence([segment]).name.get_value() + self.sequence_name = str(sequence_name).replace(" ", "_") + + self.clip_data = lib.get_segment_attributes(segment) + # segment (clip) main attributes + self.cs_name = self.clip_data["segment_name"] + self.cs_index = int(self.clip_data["segment"]) + + # get track name and index + self.track_index = int(self.clip_data["track"]) + track_name = self.clip_data["track_name"] + self.track_name = str(track_name).replace(" ", "_").replace( + "*", "noname{}".format(self.track_index)) + + # adding tag.family into tag + if kwargs.get("avalon"): + self.tag_data.update(kwargs["avalon"]) + + # add publish attribute to marker data + self.tag_data.update({"publish": True}) + + # adding ui inputs if any + self.ui_inputs = kwargs.get("ui_inputs", {}) + + # populate default data before we get other attributes + self._populate_segment_default_data() + + # use all populated default data to create all important attributes + self._populate_attributes() + + # create parents with correct types + self._create_parents() + + def convert(self): + + # solve segment data and add them to marker data + self._convert_to_marker_data() + + # if track name is in review track name and also if driving track name + # is not in review track name: skip tag creation + if (self.track_name in self.review_layer) and ( + self.driving_layer not in self.review_layer): + return + + # deal with clip name + new_name = self.tag_data.pop("newClipName") + + if self.rename: + # rename segment + self.current_segment.setName(new_name) + self.tag_data["asset"] = new_name + else: + self.tag_data["asset"] = self.cs_name + self.tag_data["hierarchyData"]["shot"] = self.cs_name + + if self.tag_data["heroTrack"] and self.review_layer: + self.tag_data.update({"reviewTrack": self.review_layer}) + else: + self.tag_data.update({"reviewTrack": None}) + + # create pype tag on track_item and add data + lib.imprint(self.current_segment, self.tag_data) + + return self.current_segment + + def _populate_segment_default_data(self): + """ Populate default formating data from segment. """ + + self.current_segment_default_data = { + "_folder_": "shots", + "_sequence_": self.sequence_name, + "_track_": self.track_name, + "_clip_": self.cs_name, + "_trackIndex_": self.track_index, + "_clipIndex_": self.cs_index + } + + def _populate_attributes(self): + """ Populate main object attributes. """ + # segment frame range and parent track name for vertical sync check + self.clip_in = int(self.clip_data["record_in"]) + self.clip_out = int(self.clip_data["record_out"]) + + # define ui inputs if non gui mode was used + self.shot_num = self.cs_index + log.debug( + "____ self.shot_num: {}".format(self.shot_num)) + + # ui_inputs data or default values if gui was not used + self.rename = self.ui_inputs.get( + "clipRename", {}).get("value") or self.rename_default + self.clip_name = self.ui_inputs.get( + "clipName", {}).get("value") or self.clip_name_default + self.hierarchy = self.ui_inputs.get( + "hierarchy", {}).get("value") or self.hierarchy_default + self.hierarchy_data = self.ui_inputs.get( + "hierarchyData", {}).get("value") or \ + self.current_segment_default_data.copy() + self.count_from = self.ui_inputs.get( + "countFrom", {}).get("value") or self.count_from_default + self.count_steps = self.ui_inputs.get( + "countSteps", {}).get("value") or self.count_steps_default + self.subset_name = self.ui_inputs.get( + "subsetName", {}).get("value") or self.subset_name_default + self.subset_family = self.ui_inputs.get( + "subsetFamily", {}).get("value") or self.subset_family_default + self.vertical_sync = self.ui_inputs.get( + "vSyncOn", {}).get("value") or self.vertical_sync_default + self.driving_layer = self.ui_inputs.get( + "vSyncTrack", {}).get("value") or self.driving_layer_default + self.review_track = self.ui_inputs.get( + "reviewTrack", {}).get("value") or self.review_track_default + self.audio = self.ui_inputs.get( + "audio", {}).get("value") or False + + # build subset name from layer name + if self.subset_name == "[ track name ]": + self.subset_name = self.track_name + + # create subset for publishing + self.subset = self.subset_family + self.subset_name.capitalize() + + def _replace_hash_to_expression(self, name, text): + """ Replace hash with number in correct padding. """ + _spl = text.split("#") + _len = (len(_spl) - 1) + _repl = "{{{0}:0>{1}}}".format(name, _len) + return text.replace(("#" * _len), _repl) + + + def _convert_to_marker_data(self): + """ Convert internal data to marker data. + + Populating the marker data into internal variable self.tag_data + """ + # define vertical sync attributes + hero_track = True + self.review_layer = "" + if self.vertical_sync and self.track_name not in self.driving_layer: + # if it is not then define vertical sync as None + hero_track = False + + # increasing steps by index of rename iteration + self.count_steps *= self.rename_index + + hierarchy_formating_data = {} + hierarchy_data = deepcopy(self.hierarchy_data) + _data = self.current_segment_default_data.copy() + if self.ui_inputs: + # adding tag metadata from ui + for _k, _v in self.ui_inputs.items(): + if _v["target"] == "tag": + self.tag_data[_k] = _v["value"] + + # driving layer is set as positive match + if hero_track or self.vertical_sync: + # mark review layer + if self.review_track and ( + self.review_track not in self.review_track_default): + # if review layer is defined and not the same as defalut + self.review_layer = self.review_track + # shot num calculate + if self.rename_index == 0: + self.shot_num = self.count_from + else: + self.shot_num = self.count_from + self.count_steps + + # clip name sequence number + _data.update({"shot": self.shot_num}) + + # solve # in test to pythonic expression + for _k, _v in hierarchy_data.items(): + if "#" not in _v["value"]: + continue + hierarchy_data[ + _k]["value"] = self._replace_hash_to_expression( + _k, _v["value"]) + + # fill up pythonic expresisons in hierarchy data + for k, _v in hierarchy_data.items(): + hierarchy_formating_data[k] = _v["value"].format(**_data) + else: + # if no gui mode then just pass default data + hierarchy_formating_data = hierarchy_data + + tag_hierarchy_data = self._solve_tag_hierarchy_data( + hierarchy_formating_data + ) + + tag_hierarchy_data.update({"heroTrack": True}) + if hero_track and self.vertical_sync: + self.vertical_clip_match.update({ + (self.clip_in, self.clip_out): tag_hierarchy_data + }) + + if not hero_track and self.vertical_sync: + # driving layer is set as negative match + for (_in, _out), hero_data in self.vertical_clip_match.items(): + hero_data.update({"heroTrack": False}) + if _in == self.clip_in and _out == self.clip_out: + data_subset = hero_data["subset"] + # add track index in case duplicity of names in hero data + if self.subset in data_subset: + hero_data["subset"] = self.subset + str( + self.track_index) + # in case track name and subset name is the same then add + if self.subset_name == self.track_name: + hero_data["subset"] = self.subset + # assing data to return hierarchy data to tag + tag_hierarchy_data = hero_data + + # add data to return data dict + self.tag_data.update(tag_hierarchy_data) + + def _solve_tag_hierarchy_data(self, hierarchy_formating_data): + """ Solve marker data from hierarchy data and templates. """ + # fill up clip name and hierarchy keys + hierarchy_filled = self.hierarchy.format(**hierarchy_formating_data) + clip_name_filled = self.clip_name.format(**hierarchy_formating_data) + + # remove shot from hierarchy data: is not needed anymore + hierarchy_formating_data.pop("shot") + + return { + "newClipName": clip_name_filled, + "hierarchy": hierarchy_filled, + "parents": self.parents, + "hierarchyData": hierarchy_formating_data, + "subset": self.subset, + "family": self.subset_family, + "families": [self.data["family"]] + } + + def _convert_to_entity(self, type, template): + """ Converting input key to key with type. """ + # convert to entity type + entity_type = self.types.get(type, None) + + assert entity_type, "Missing entity type for `{}`".format( + type + ) + + # first collect formating data to use for formating template + formating_data = {} + for _k, _v in self.hierarchy_data.items(): + value = _v["value"].format( + **self.current_segment_default_data) + formating_data[_k] = value + + return { + "entity_type": entity_type, + "entity_name": template.format( + **formating_data + ) + } + + def _create_parents(self): + """ Create parents and return it in list. """ + self.parents = [] + + patern = re.compile(self.parents_search_patern) + + par_split = [(patern.findall(t).pop(), t) + for t in self.hierarchy.split("/")] + + for type, template in par_split: + parent = self._convert_to_entity(type, template) + self.parents.append(parent) + + # Publishing plugin functions # Loader plugin functions diff --git a/openpype/hosts/flame/api/style.css b/openpype/hosts/flame/api/style.css new file mode 100644 index 00000000000..b64c391c6e9 --- /dev/null +++ b/openpype/hosts/flame/api/style.css @@ -0,0 +1,26 @@ +QWidget { + font-size: 13px; +} + +QSpinBox { + padding: 2; + max-width: 8em; +} + +QLineEdit { + padding: 2; + min-width: 15em; +} + +QVBoxLayout { + min-width: 15em; + background-color: #201f1f; +} + +QComboBox { + min-width: 8em; +} + +#sectionContent { + background-color: #2E2D2D; +} \ No newline at end of file From 25e0bffe5854cbfbcfd43d8e0001c8d4471e2ee3 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 6 Jan 2022 14:04:01 +0100 Subject: [PATCH 378/492] flame: adding create plugin for publishable clips --- .../flame/plugins/create/create_shot_clip.py | 268 ++++++++++++++++++ 1 file changed, 268 insertions(+) create mode 100644 openpype/hosts/flame/plugins/create/create_shot_clip.py diff --git a/openpype/hosts/flame/plugins/create/create_shot_clip.py b/openpype/hosts/flame/plugins/create/create_shot_clip.py new file mode 100644 index 00000000000..71ea9b3c86c --- /dev/null +++ b/openpype/hosts/flame/plugins/create/create_shot_clip.py @@ -0,0 +1,268 @@ +from copy import deepcopy +import openpype.hosts.flame as opflame +import openpype.hosts.flame.api.plugin as fplugin +import openpype.hosts.flame.api.lib as flib +reload(fplugin) +reload(flib) + +def _get_video_track_names(sequence): + track_names = [] + for ver in sequence.versions: + for track in ver.tracks: + track_names.append(track.name.get_value()) + +class CreateShotClip(fplugin.Creator): + """Publishable clip""" + + label = "Create Publishable Clip" + family = "clip" + icon = "film" + defaults = ["Main"] + + gui_tracks = _get_video_track_names( + flib.get_current_sequence(opflame.selection) + ) + gui_name = "Pype publish attributes creator" + gui_info = "Define sequential rename and fill hierarchy data." + gui_inputs = { + "renameHierarchy": { + "type": "section", + "label": "Shot Hierarchy And Rename Settings", + "target": "ui", + "order": 0, + "value": { + "hierarchy": { + "value": "{folder}/{sequence}", + "type": "QLineEdit", + "label": "Shot Parent Hierarchy", + "target": "tag", + "toolTip": "Parents folder for shot root folder, Template filled with `Hierarchy Data` section", # noqa + "order": 0}, + "clipRename": { + "value": False, + "type": "QCheckBox", + "label": "Rename clips", + "target": "ui", + "toolTip": "Renaming selected clips on fly", # noqa + "order": 1}, + "clipName": { + "value": "{sequence}{shot}", + "type": "QLineEdit", + "label": "Clip Name Template", + "target": "ui", + "toolTip": "template for creating shot namespaused for renaming (use rename: on)", # noqa + "order": 2}, + "countFrom": { + "value": 10, + "type": "QSpinBox", + "label": "Count sequence from", + "target": "ui", + "toolTip": "Set when the sequence number stafrom", # noqa + "order": 3}, + "countSteps": { + "value": 10, + "type": "QSpinBox", + "label": "Stepping number", + "target": "ui", + "toolTip": "What number is adding every new step", # noqa + "order": 4}, + } + }, + "hierarchyData": { + "type": "dict", + "label": "Shot Template Keywords", + "target": "tag", + "order": 1, + "value": { + "folder": { + "value": "shots", + "type": "QLineEdit", + "label": "{folder}", + "target": "tag", + "toolTip": "Name of folder used for root of generated shots.\nUsable tokens:\n\t{_clip_}: name of used clip\n\t{_track_}: name of parent track layer\n\t{_sequence_}: name of parent sequence (timeline)", # noqa + "order": 0}, + "episode": { + "value": "ep01", + "type": "QLineEdit", + "label": "{episode}", + "target": "tag", + "toolTip": "Name of episode.\nUsable tokens:\n\t{_clip_}: name of used clip\n\t{_track_}: name of parent track layer\n\t{_sequence_}: name of parent sequence (timeline)", # noqa + "order": 1}, + "sequence": { + "value": "sq01", + "type": "QLineEdit", + "label": "{sequence}", + "target": "tag", + "toolTip": "Name of sequence of shots.\nUsable tokens:\n\t{_clip_}: name of used clip\n\t{_track_}: name of parent track layer\n\t{_sequence_}: name of parent sequence (timeline)", # noqa + "order": 2}, + "track": { + "value": "{_track_}", + "type": "QLineEdit", + "label": "{track}", + "target": "tag", + "toolTip": "Name of sequence of shots.\nUsable tokens:\n\t{_clip_}: name of used clip\n\t{_track_}: name of parent track layer\n\t{_sequence_}: name of parent sequence (timeline)", # noqa + "order": 3}, + "shot": { + "value": "sh###", + "type": "QLineEdit", + "label": "{shot}", + "target": "tag", + "toolTip": "Name of shot. `#` is converted to paded number. \nAlso could be used with usable tokens:\n\t{_clip_}: name of used clip\n\t{_track_}: name of parent track layer\n\t{_sequence_}: name of parent sequence (timeline)", # noqa + "order": 4} + } + }, + "verticalSync": { + "type": "section", + "label": "Vertical Synchronization Of Attributes", + "target": "ui", + "order": 2, + "value": { + "vSyncOn": { + "value": True, + "type": "QCheckBox", + "label": "Enable Vertical Sync", + "target": "ui", + "toolTip": "Switch on if you want clips above each other to share its attributes", # noqa + "order": 0}, + "vSyncTrack": { + "value": gui_tracks, # noqa + "type": "QComboBox", + "label": "Hero track", + "target": "ui", + "toolTip": "Select driving track name which should be hero for all others", # noqa + "order": 1} + } + }, + "publishSettings": { + "type": "section", + "label": "Publish Settings", + "target": "ui", + "order": 3, + "value": { + "subsetName": { + "value": ["", "main", "bg", "fg", "bg", + "animatic"], + "type": "QComboBox", + "label": "Subset Name", + "target": "ui", + "toolTip": "chose subset name patern, if is selected, name of track layer will be used", # noqa + "order": 0}, + "subsetFamily": { + "value": ["plate", "take"], + "type": "QComboBox", + "label": "Subset Family", + "target": "ui", "toolTip": "What use of this subset is for", # noqa + "order": 1}, + "reviewTrack": { + "value": ["< none >"] + gui_tracks, + "type": "QComboBox", + "label": "Use Review Track", + "target": "ui", + "toolTip": "Generate preview videos on fly, if `< none >` is defined nothing will be generated.", # noqa + "order": 2}, + "audio": { + "value": False, + "type": "QCheckBox", + "label": "Include audio", + "target": "tag", + "toolTip": "Process subsets with corresponding audio", # noqa + "order": 3}, + "sourceResolution": { + "value": False, + "type": "QCheckBox", + "label": "Source resolution", + "target": "tag", + "toolTip": "Is resloution taken from timeline or source?", # noqa + "order": 4}, + } + }, + "frameRangeAttr": { + "type": "section", + "label": "Shot Attributes", + "target": "ui", + "order": 4, + "value": { + "workfileFrameStart": { + "value": 1001, + "type": "QSpinBox", + "label": "Workfiles Start Frame", + "target": "tag", + "toolTip": "Set workfile starting frame number", # noqa + "order": 0 + }, + "handleStart": { + "value": 0, + "type": "QSpinBox", + "label": "Handle Start", + "target": "tag", + "toolTip": "Handle at start of clip", # noqa + "order": 1 + }, + "handleEnd": { + "value": 0, + "type": "QSpinBox", + "label": "Handle End", + "target": "tag", + "toolTip": "Handle at end of clip", # noqa + "order": 2 + } + } + } + } + + presets = None + + def process(self): + # Creator copy of object attributes that are modified during `process` + presets = deepcopy(self.presets) + gui_inputs = deepcopy(self.gui_inputs) + + # get key pares from presets and match it on ui inputs + for k, v in gui_inputs.items(): + if v["type"] in ("dict", "section"): + # nested dictionary (only one level allowed + # for sections and dict) + for _k, _v in v["value"].items(): + if presets.get(_k): + gui_inputs[k][ + "value"][_k]["value"] = presets[_k] + if presets.get(k): + gui_inputs[k]["value"] = presets[k] + + # open widget for plugins inputs + widget = self.widget(self.gui_name, self.gui_info, gui_inputs) + widget.exec_() + + if len(self.selected) < 1: + return + + if not widget.result: + print("Operation aborted") + return + + self.rename_add = 0 + + # get ui output for track name for vertical sync + v_sync_track = widget.result["vSyncTrack"]["value"] + + # sort selected trackItems by + sorted_selected_segments = [] + unsorted_selected_segments = [] + for _segment in self.selected: + if _segment.parent.name.get_value() in v_sync_track: + sorted_selected_segments.append(_segment) + else: + unsorted_selected_segments.append(_segment) + + sorted_selected_segments.extend(unsorted_selected_segments) + + kwargs = { + "ui_inputs": widget.result, + "avalon": self.data + } + + for i, segment in enumerate(sorted_selected_segments): + self.rename_index = i + + # convert track item to timeline media pool item + fplugin.PublishableClip(self, segment, **kwargs).convert() From e2ab00c54a91ffbf3d07c37b26d8a9f06e487f58 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 6 Jan 2022 14:17:32 +0100 Subject: [PATCH 379/492] flame: tuning creator --- openpype/hosts/flame/api/plugin.py | 35 +++++++++++++++--------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index 1a3880a19a5..34e626b099f 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -3,7 +3,7 @@ from Qt import QtWidgets, QtCore import openpype.api as openpype import openpype.hosts.flame as opflame -from . import lib +from . import lib, pipeline from copy import deepcopy log = openpype.Logger().get_logger(__name__) @@ -321,8 +321,8 @@ class PublishableClip: Returns: flame.PySegment: flame api object """ - vertical_clip_match = dict() - tag_data = dict() + vertical_clip_match = {} + marker_data = {} types = { "shot": "shot", "folder": "folder", @@ -368,10 +368,10 @@ def __init__(self, cls, segment, **kwargs): # adding tag.family into tag if kwargs.get("avalon"): - self.tag_data.update(kwargs["avalon"]) + self.marker_data.update(kwargs["avalon"]) # add publish attribute to marker data - self.tag_data.update({"publish": True}) + self.marker_data.update({"publish": True}) # adding ui inputs if any self.ui_inputs = kwargs.get("ui_inputs", {}) @@ -397,23 +397,23 @@ def convert(self): return # deal with clip name - new_name = self.tag_data.pop("newClipName") + new_name = self.marker_data.pop("newClipName") if self.rename: # rename segment - self.current_segment.setName(new_name) - self.tag_data["asset"] = new_name + self.current_segment.name = new_name + self.marker_data["asset"] = new_name else: - self.tag_data["asset"] = self.cs_name - self.tag_data["hierarchyData"]["shot"] = self.cs_name + self.marker_data["asset"] = self.cs_name + self.marker_data["hierarchyData"]["shot"] = self.cs_name - if self.tag_data["heroTrack"] and self.review_layer: - self.tag_data.update({"reviewTrack": self.review_layer}) + if self.marker_data["heroTrack"] and self.review_layer: + self.marker_data.update({"reviewTrack": self.review_layer}) else: - self.tag_data.update({"reviewTrack": None}) + self.marker_data.update({"reviewTrack": None}) # create pype tag on track_item and add data - lib.imprint(self.current_segment, self.tag_data) + pipeline.imprint(self.current_segment, self.marker_data) return self.current_segment @@ -481,11 +481,10 @@ def _replace_hash_to_expression(self, name, text): _repl = "{{{0}:0>{1}}}".format(name, _len) return text.replace(("#" * _len), _repl) - def _convert_to_marker_data(self): """ Convert internal data to marker data. - Populating the marker data into internal variable self.tag_data + Populating the marker data into internal variable self.marker_data """ # define vertical sync attributes hero_track = True @@ -504,7 +503,7 @@ def _convert_to_marker_data(self): # adding tag metadata from ui for _k, _v in self.ui_inputs.items(): if _v["target"] == "tag": - self.tag_data[_k] = _v["value"] + self.marker_data[_k] = _v["value"] # driving layer is set as positive match if hero_track or self.vertical_sync: @@ -564,7 +563,7 @@ def _convert_to_marker_data(self): tag_hierarchy_data = hero_data # add data to return data dict - self.tag_data.update(tag_hierarchy_data) + self.marker_data.update(tag_hierarchy_data) def _solve_tag_hierarchy_data(self, hierarchy_formating_data): """ Solve marker data from hierarchy data and templates. """ From a2bb8f1839ca253daa962a876719e94262115f6e Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 6 Jan 2022 14:59:59 +0100 Subject: [PATCH 380/492] flame: beautification of code --- openpype/hosts/flame/api/pipeline.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/openpype/hosts/flame/api/pipeline.py b/openpype/hosts/flame/api/pipeline.py index 22955896270..99a33e4cb9e 100644 --- a/openpype/hosts/flame/api/pipeline.py +++ b/openpype/hosts/flame/api/pipeline.py @@ -22,7 +22,6 @@ def install(): CREATE_PATH, INVENTORY_PATH ) - # TODO: install # Disable all families except for the ones we explicitly want to see family_states = [ @@ -36,19 +35,18 @@ def install(): avalon.data["familiesStateDefault"] = False avalon.data["familiesStateToggled"] = family_states - log.info("openpype.hosts.flame installed") pyblish.register_host("flame") pyblish.register_plugin_path(PUBLISH_PATH) - log.info("Registering Flame plug-ins..") - avalon.register_plugin_path(avalon.Loader, LOAD_PATH) avalon.register_plugin_path(avalon.Creator, CREATE_PATH) avalon.register_plugin_path(avalon.InventoryAction, INVENTORY_PATH) + log.info("OpenPype Flame plug-ins registred ...") # register callback for switching publishable pyblish.register_callback("instanceToggled", on_pyblish_instance_toggled) + log.info("OpenPype Flame host installed ...") def uninstall(): from .. import ( @@ -58,11 +56,10 @@ def uninstall(): INVENTORY_PATH ) - # TODO: uninstall pyblish.deregister_host("flame") - pyblish.deregister_plugin_path(PUBLISH_PATH) - log.info("Deregistering DaVinci Resovle plug-ins..") + log.info("Deregistering Flame plug-ins..") + pyblish.deregister_plugin_path(PUBLISH_PATH) avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH) avalon.deregister_plugin_path(avalon.Creator, CREATE_PATH) avalon.deregister_plugin_path(avalon.InventoryAction, INVENTORY_PATH) @@ -70,6 +67,8 @@ def uninstall(): # register callback for switching publishable pyblish.deregister_callback("instanceToggled", on_pyblish_instance_toggled) + log.info("OpenPype Flame host uninstalled ...") + def containerise(tl_segment, name, From 2f0fcaebe150b307e598b070bd996568eec1c2ac Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 6 Jan 2022 15:00:15 +0100 Subject: [PATCH 381/492] flame: fix missing return --- openpype/hosts/flame/plugins/create/create_shot_clip.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/hosts/flame/plugins/create/create_shot_clip.py b/openpype/hosts/flame/plugins/create/create_shot_clip.py index 71ea9b3c86c..79afca507fe 100644 --- a/openpype/hosts/flame/plugins/create/create_shot_clip.py +++ b/openpype/hosts/flame/plugins/create/create_shot_clip.py @@ -11,6 +11,8 @@ def _get_video_track_names(sequence): for track in ver.tracks: track_names.append(track.name.get_value()) + return track_names + class CreateShotClip(fplugin.Creator): """Publishable clip""" From 5c11089aff32e09c17439743290b50a4097c8714 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 6 Jan 2022 15:00:37 +0100 Subject: [PATCH 382/492] flame: testing plugin refactory --- .../plugins/publish/collect_test_selection.py | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/flame/plugins/publish/collect_test_selection.py b/openpype/hosts/flame/plugins/publish/collect_test_selection.py index d30d6ed331c..cd7355d1f5e 100644 --- a/openpype/hosts/flame/plugins/publish/collect_test_selection.py +++ b/openpype/hosts/flame/plugins/publish/collect_test_selection.py @@ -1,9 +1,11 @@ +import os import pyblish.api import openpype.hosts.flame as opflame from openpype.hosts.flame.otio import flame_export as otio_export -from openpype.hosts.flame.api import lib +from openpype.hosts.flame.api import lib, pipeline from pprint import pformat reload(lib) # noqa +reload(pipeline) # noqa reload(otio_export) # noqa @@ -17,14 +19,30 @@ class CollectTestSelection(pyblish.api.ContextPlugin): hosts = ["flame"] def process(self, context): - self.log.info(opflame.selection) + self.log.info( + "Active Selection: {}".format(opflame.selection)) sequence = lib.get_current_sequence(opflame.selection) + self.test_imprint_data(sequence) + self.test_otio_export(sequence) + + def test_otio_export(self, sequence): + home_dir = os.path.expanduser("~") + export_path = os.path.normalize( + os.path.join( + home_dir, "otio_timeline_export.otio" + ) + ) otio_timeline = otio_export.create_otio_timeline(sequence) + otio_export.write_to_file( + otio_timeline, export_path + ) self.log.info(pformat(otio_timeline)) + self.log.info("Otio exported to: {}".format(export_path)) + def test_imprint_data(self, sequence): # test segment markers for ver in sequence.versions: for track in ver.tracks: @@ -40,8 +58,8 @@ def process(self, context): self.log.debug("Segment with OpenPypeData: {}".format( segment.name)) - lib.imprint(segment, { - 'asset': 'sq020sh0280', + pipeline.imprint(segment, { + 'asset': segment.name.get_value(), 'family': 'render', 'subset': 'subsetMain' }) From c67e672acaaa0ed5a459ef0e52a1cd81b6ee049d Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 6 Jan 2022 15:08:06 +0100 Subject: [PATCH 383/492] flame: improving testing plugin adding maintained seqment selection functionality --- openpype/hosts/flame/api/lib.py | 2 +- .../plugins/publish/collect_test_selection.py | 27 +++++++------------ 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index 2d30390d219..e5642dd6f93 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -14,7 +14,7 @@ class ctx: # OpenPype marker workflow variables marker_name = "OpenPypeData" marker_duration = 0 - marker_color = "red" + marker_color = "cyan" publish_default = False color_map = { "red": (1.0, 0.0, 0.0), diff --git a/openpype/hosts/flame/plugins/publish/collect_test_selection.py b/openpype/hosts/flame/plugins/publish/collect_test_selection.py index cd7355d1f5e..875ef34a074 100644 --- a/openpype/hosts/flame/plugins/publish/collect_test_selection.py +++ b/openpype/hosts/flame/plugins/publish/collect_test_selection.py @@ -43,23 +43,16 @@ def test_otio_export(self, sequence): self.log.info("Otio exported to: {}".format(export_path)) def test_imprint_data(self, sequence): - # test segment markers - for ver in sequence.versions: - for track in ver.tracks: - if len(track.segments) == 0 and track.hidden: + with lib.maintained_segment_selection(sequence) as selected_segments: + for segment in selected_segments: + if str(segment.name)[1:-1] == "": continue - for segment in track.segments: - if str(segment.name)[1:-1] == "": - continue - if not segment.selected: - continue + self.log.debug("Segment with OpenPypeData: {}".format( + segment.name)) - self.log.debug("Segment with OpenPypeData: {}".format( - segment.name)) - - pipeline.imprint(segment, { - 'asset': segment.name.get_value(), - 'family': 'render', - 'subset': 'subsetMain' - }) + pipeline.imprint(segment, { + 'asset': segment.name.get_value(), + 'family': 'render', + 'subset': 'subsetMain' + }) From 12f9eb2c328259c105cfe036cee08a14016be0a5 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 6 Jan 2022 15:09:41 +0100 Subject: [PATCH 384/492] flame: fix normalize to normpath --- openpype/hosts/flame/plugins/publish/collect_test_selection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/flame/plugins/publish/collect_test_selection.py b/openpype/hosts/flame/plugins/publish/collect_test_selection.py index 875ef34a074..29ca08d9b51 100644 --- a/openpype/hosts/flame/plugins/publish/collect_test_selection.py +++ b/openpype/hosts/flame/plugins/publish/collect_test_selection.py @@ -29,7 +29,7 @@ def process(self, context): def test_otio_export(self, sequence): home_dir = os.path.expanduser("~") - export_path = os.path.normalize( + export_path = os.path.normpath( os.path.join( home_dir, "otio_timeline_export.otio" ) From abc39a9e9f04d7efbda4b1548966794090c6f077 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 6 Jan 2022 15:17:02 +0100 Subject: [PATCH 385/492] make sure output is always the same --- openpype/lib/python_module_tools.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/openpype/lib/python_module_tools.py b/openpype/lib/python_module_tools.py index 69da4cc6619..903af01676a 100644 --- a/openpype/lib/python_module_tools.py +++ b/openpype/lib/python_module_tools.py @@ -59,22 +59,23 @@ def modules_from_path(folder_path): """ crashed = [] modules = [] + output = (modules, crashed) # Just skip and return empty list if path is not set if not folder_path: - return modules + return output # Do not allow relative imports if folder_path.startswith("."): log.warning(( "BUG: Relative paths are not allowed for security reasons. {}" ).format(folder_path)) - return modules + return output folder_path = os.path.normpath(folder_path) if not os.path.isdir(folder_path): log.warning("Not a directory path: {}".format(folder_path)) - return modules + return output for filename in os.listdir(folder_path): # Ignore files which start with underscore @@ -101,7 +102,7 @@ def modules_from_path(folder_path): ) continue - return modules, crashed + return output def recursive_bases_from_class(klass): From 3f5b04161987ec15f937a5c0784b7389595209b3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 6 Jan 2022 15:17:18 +0100 Subject: [PATCH 386/492] update docstring --- openpype/lib/python_module_tools.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/openpype/lib/python_module_tools.py b/openpype/lib/python_module_tools.py index 903af01676a..f62c848e4a2 100644 --- a/openpype/lib/python_module_tools.py +++ b/openpype/lib/python_module_tools.py @@ -49,13 +49,10 @@ def modules_from_path(folder_path): Arguments: path (str): Path to folder containing python scripts. - return_crasher (bool): Crashed module paths with exception info - will be returned too. Returns: - list, tuple: List of modules when `return_crashed` is False else tuple - with list of modules at first place and tuple of path and exception - info at second place. + tuple: First list contains successfully imported modules + and second list contains tuples of path and exception. """ crashed = [] modules = [] From 02c41986250d1cc05dada82e4e32afc3e238536d Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 6 Jan 2022 15:20:58 +0100 Subject: [PATCH 387/492] flame: settings for creator plugin --- .../defaults/project_settings/flame.json | 20 +++ .../schemas/projects_schema/schema_main.json | 4 + .../projects_schema/schema_project_flame.json | 124 ++++++++++++++++++ 3 files changed, 148 insertions(+) create mode 100644 openpype/settings/defaults/project_settings/flame.json create mode 100644 openpype/settings/entities/schemas/projects_schema/schema_project_flame.json diff --git a/openpype/settings/defaults/project_settings/flame.json b/openpype/settings/defaults/project_settings/flame.json new file mode 100644 index 00000000000..b6fbdecc955 --- /dev/null +++ b/openpype/settings/defaults/project_settings/flame.json @@ -0,0 +1,20 @@ +{ + "create": { + "CreateShotClip": { + "hierarchy": "{folder}/{sequence}", + "clipRename": true, + "clipName": "{track}{sequence}{shot}", + "countFrom": 10, + "countSteps": 10, + "folder": "shots", + "episode": "ep01", + "sequence": "sq01", + "track": "{_track_}", + "shot": "sh###", + "vSyncOn": false, + "workfileFrameStart": 1001, + "handleStart": 10, + "handleEnd": 10 + } + } +} \ No newline at end of file diff --git a/openpype/settings/entities/schemas/projects_schema/schema_main.json b/openpype/settings/entities/schemas/projects_schema/schema_main.json index c9eca5dedd3..8a2ad451ee7 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_main.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_main.json @@ -110,6 +110,10 @@ "type": "schema", "name": "schema_project_celaction" }, + { + "type": "schema", + "name": "schema_project_flame" + }, { "type": "schema", "name": "schema_project_resolve" diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json b/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json new file mode 100644 index 00000000000..d713c376207 --- /dev/null +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json @@ -0,0 +1,124 @@ +{ + "type": "dict", + "collapsible": true, + "key": "flame", + "label": "Flame", + "is_file": true, + "children": [ + { + "type": "dict", + "collapsible": true, + "key": "create", + "label": "Create plugins", + "children": [ + { + "type": "dict", + "collapsible": true, + "key": "CreateShotClip", + "label": "Create Shot Clip", + "is_group": true, + "children": [ + { + "type": "collapsible-wrap", + "label": "Shot Hierarchy And Rename Settings", + "collapsible": false, + "children": [ + { + "type": "text", + "key": "hierarchy", + "label": "Shot parent hierarchy" + }, + { + "type": "boolean", + "key": "clipRename", + "label": "Rename clips" + }, + { + "type": "text", + "key": "clipName", + "label": "Clip name template" + }, + { + "type": "number", + "key": "countFrom", + "label": "Count sequence from" + }, + { + "type": "number", + "key": "countSteps", + "label": "Stepping number" + } + ] + }, + { + "type": "collapsible-wrap", + "label": "Shot Template Keywords", + "collapsible": false, + "children": [ + { + "type": "text", + "key": "folder", + "label": "{folder}" + }, + { + "type": "text", + "key": "episode", + "label": "{episode}" + }, + { + "type": "text", + "key": "sequence", + "label": "{sequence}" + }, + { + "type": "text", + "key": "track", + "label": "{track}" + }, + { + "type": "text", + "key": "shot", + "label": "{shot}" + } + ] + }, + { + "type": "collapsible-wrap", + "label": "Vertical Synchronization Of Attributes", + "collapsible": false, + "children": [ + { + "type": "boolean", + "key": "vSyncOn", + "label": "Enable Vertical Sync" + } + ] + }, + { + "type": "collapsible-wrap", + "label": "Shot Attributes", + "collapsible": false, + "children": [ + { + "type": "number", + "key": "workfileFrameStart", + "label": "Workfiles Start Frame" + }, + { + "type": "number", + "key": "handleStart", + "label": "Handle start (head)" + }, + { + "type": "number", + "key": "handleEnd", + "label": "Handle end (tail)" + } + ] + } + ] + } + ] + } + ] +} From 3aa0efdc4dacee60e7995a4d32bfd675c0ae0afd Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 6 Jan 2022 15:29:59 +0100 Subject: [PATCH 388/492] flame: do not alter project attributes if it exists already --- openpype/hosts/flame/api/scripts/wiretap_com.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/flame/api/scripts/wiretap_com.py b/openpype/hosts/flame/api/scripts/wiretap_com.py index 5f7b2580e66..f1b5ab22363 100644 --- a/openpype/hosts/flame/api/scripts/wiretap_com.py +++ b/openpype/hosts/flame/api/scripts/wiretap_com.py @@ -89,9 +89,11 @@ def get_launch_args( workspace_name = kwargs.get("workspace_name") color_policy = kwargs.get("color_policy") - self._project_prep(project_name) - self._set_project_settings(project_name, project_data) - self._set_project_colorspace(project_name, color_policy) + project_exists = self._project_prep(project_name) + if not project_exists: + self._set_project_settings(project_name, project_data) + self._set_project_colorspace(project_name, color_policy) + user_name = self._user_prep(user_name) if workspace_name is None: @@ -207,6 +209,7 @@ def _project_prep(self, project_name): print( "A new project '{}' is created.".format(project_name)) + return project_exists def _get_all_volumes(self): """Request all available volumens from WireTap From e3122b9782fb1bb1ce17e6883a8d35a2c86ce12f Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 6 Jan 2022 15:36:41 +0100 Subject: [PATCH 389/492] OP-2282 - added error when no layers in Background Background import without layers failed with nondescript message. Fixed broken layer naming --- .../aftereffects/plugins/load/load_background.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/aftereffects/plugins/load/load_background.py b/openpype/hosts/aftereffects/plugins/load/load_background.py index 9856abe3fed..4d3d46a4428 100644 --- a/openpype/hosts/aftereffects/plugins/load/load_background.py +++ b/openpype/hosts/aftereffects/plugins/load/load_background.py @@ -22,21 +22,23 @@ class BackgroundLoader(api.Loader): def load(self, context, name=None, namespace=None, data=None): items = stub.get_items(comps=True) - existing_items = [layer.name for layer in items] + existing_items = [layer.name.replace(stub.LOADED_ICON, '') + for layer in items] comp_name = get_unique_layer_name( existing_items, "{}_{}".format(context["asset"]["name"], name)) layers = get_background_layers(self.fname) + if not layers: + raise ValueError("No layers found in {}".format(self.fname)) + comp = stub.import_background(None, stub.LOADED_ICON + comp_name, layers) if not comp: - self.log.warning( - "Import background failed.") - self.log.warning("Check host app for alert error.") - return + raise ValueError("Import background failed. " + "Please contact support") self[:] = [comp] namespace = namespace or comp_name From f6b9d122442d8cd95b4b9307a7d2cad4466fdddd Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 6 Jan 2022 15:57:45 +0100 Subject: [PATCH 390/492] flame: host maintained selection --- openpype/hosts/flame/__init__.py | 4 +++- openpype/hosts/flame/api/pipeline.py | 18 +++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/flame/__init__.py b/openpype/hosts/flame/__init__.py index 691b6f8119d..da42b313aab 100644 --- a/openpype/hosts/flame/__init__.py +++ b/openpype/hosts/flame/__init__.py @@ -10,7 +10,8 @@ update_container, remove_instance, list_instances, - imprint + imprint, + maintained_selection ) from .api.lib import ( @@ -87,6 +88,7 @@ "remove_instance", "list_instances", "imprint", + "maintained_selection", # utils "setup", diff --git a/openpype/hosts/flame/api/pipeline.py b/openpype/hosts/flame/api/pipeline.py index 99a33e4cb9e..ee0e12584a1 100644 --- a/openpype/hosts/flame/api/pipeline.py +++ b/openpype/hosts/flame/api/pipeline.py @@ -7,7 +7,9 @@ from openpype.api import Logger from .lib import ( set_segment_data_marker, - set_publish_attribute + set_publish_attribute, + maintained_segment_selection, + get_current_sequence ) AVALON_CONTAINERS = "AVALON_CONTAINERS" @@ -151,3 +153,17 @@ def imprint(segment, data=None): # add publish attribute set_publish_attribute(segment, True) + +@contextlib.contextmanager +def maintained_selection(): + import flame + from .. import selection + + # check if segment is selected + if isinstance(selection[0], flame.PySegment): + sequence = get_current_sequence(selection) + try: + with maintained_segment_selection(sequence): + yield + finally: + pass \ No newline at end of file From 613e9ff2fb123b4e8606143a9a0af744cc384026 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 6 Jan 2022 16:02:30 +0100 Subject: [PATCH 391/492] renamed get_pype_execute_args to get_openpype_execute_args --- openpype/lib/__init__.py | 2 ++ openpype/lib/execute.py | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/openpype/lib/__init__.py b/openpype/lib/__init__.py index 34926453cb7..f721e0f5774 100644 --- a/openpype/lib/__init__.py +++ b/openpype/lib/__init__.py @@ -24,6 +24,7 @@ from .terminal import Terminal from .execute import ( + get_openpype_execute_args, get_pype_execute_args, get_linux_launcher_args, execute, @@ -173,6 +174,7 @@ terminal = Terminal __all__ = [ + "get_openpype_execute_args", "get_pype_execute_args", "get_linux_launcher_args", "execute", diff --git a/openpype/lib/execute.py b/openpype/lib/execute.py index f97617d906f..452a8fd4c0d 100644 --- a/openpype/lib/execute.py +++ b/openpype/lib/execute.py @@ -147,6 +147,11 @@ def path_to_subprocess_arg(path): def get_pype_execute_args(*args): + """Backwards compatible function for 'get_openpype_execute_args'.""" + return get_openpype_execute_args(*args) + + +def get_openpype_execute_args(*args): """Arguments to run pype command. Arguments for subprocess when need to spawn new pype process. Which may be From b3082d9e211aa2245262654d52eb0f89758db04a Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 6 Jan 2022 16:08:57 +0100 Subject: [PATCH 392/492] flame: track name preset fix --- openpype/hosts/flame/plugins/create/create_shot_clip.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/flame/plugins/create/create_shot_clip.py b/openpype/hosts/flame/plugins/create/create_shot_clip.py index 79afca507fe..45c4557dad6 100644 --- a/openpype/hosts/flame/plugins/create/create_shot_clip.py +++ b/openpype/hosts/flame/plugins/create/create_shot_clip.py @@ -142,12 +142,12 @@ class CreateShotClip(fplugin.Creator): "order": 3, "value": { "subsetName": { - "value": ["", "main", "bg", "fg", "bg", + "value": ["[ track name ]", "main", "bg", "fg", "bg", "animatic"], "type": "QComboBox", "label": "Subset Name", "target": "ui", - "toolTip": "chose subset name patern, if is selected, name of track layer will be used", # noqa + "toolTip": "chose subset name patern, if [ track name ] is selected, name of track layer will be used", # noqa "order": 0}, "subsetFamily": { "value": ["plate", "take"], From f48b864e2359398c960debd2ab0a9668792ca00c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 6 Jan 2022 16:12:38 +0100 Subject: [PATCH 393/492] Flame: fixing wrong type --- openpype/hosts/flame/api/plugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index 34e626b099f..5e47ce3a688 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -401,8 +401,8 @@ def convert(self): if self.rename: # rename segment - self.current_segment.name = new_name - self.marker_data["asset"] = new_name + self.current_segment.name = str(new_name) + self.marker_data["asset"] = str(new_name) else: self.marker_data["asset"] = self.cs_name self.marker_data["hierarchyData"]["shot"] = self.cs_name From 3c1967f080504e1be3916d1deeb2241c234091e8 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 6 Jan 2022 16:13:30 +0100 Subject: [PATCH 394/492] use get_openpype_execute_args instead of get_pype_execute_args --- openpype/hooks/pre_non_python_host_launch.py | 5 ++--- openpype/hosts/tvpaint/hooks/pre_launch_args.py | 4 ++-- openpype/lib/pype_info.py | 4 ++-- .../ftrack/ftrack_server/event_server_cli.py | 6 +++--- .../default_modules/ftrack/ftrack_server/socket_thread.py | 4 ++-- openpype/modules/standalonepublish_action.py | 4 ++-- .../tools/standalonepublish/widgets/widget_components.py | 4 ++-- openpype/tools/tray/pype_tray.py | 6 +++--- 8 files changed, 18 insertions(+), 19 deletions(-) diff --git a/openpype/hooks/pre_non_python_host_launch.py b/openpype/hooks/pre_non_python_host_launch.py index 848ed675a82..29e40d28c8a 100644 --- a/openpype/hooks/pre_non_python_host_launch.py +++ b/openpype/hooks/pre_non_python_host_launch.py @@ -3,7 +3,7 @@ from openpype.lib import ( PreLaunchHook, - get_pype_execute_args + get_openpype_execute_args ) from openpype import PACKAGE_DIR as OPENPYPE_DIR @@ -35,7 +35,7 @@ def execute(self): "non_python_host_launch.py" ) - new_launch_args = get_pype_execute_args( + new_launch_args = get_openpype_execute_args( "run", script_path, executable_path ) # Add workfile path if exists @@ -48,4 +48,3 @@ def execute(self): if remainders: self.launch_context.launch_args.extend(remainders) - diff --git a/openpype/hosts/tvpaint/hooks/pre_launch_args.py b/openpype/hosts/tvpaint/hooks/pre_launch_args.py index 62fd662d79f..2a8f49d5b09 100644 --- a/openpype/hosts/tvpaint/hooks/pre_launch_args.py +++ b/openpype/hosts/tvpaint/hooks/pre_launch_args.py @@ -4,7 +4,7 @@ from openpype.hosts import tvpaint from openpype.lib import ( PreLaunchHook, - get_pype_execute_args + get_openpype_execute_args ) import avalon @@ -30,7 +30,7 @@ def execute(self): while self.launch_context.launch_args: remainders.append(self.launch_context.launch_args.pop(0)) - new_launch_args = get_pype_execute_args( + new_launch_args = get_openpype_execute_args( "run", self.launch_script_path(), executable_path ) diff --git a/openpype/lib/pype_info.py b/openpype/lib/pype_info.py index 33715e369d0..15856bfb19d 100644 --- a/openpype/lib/pype_info.py +++ b/openpype/lib/pype_info.py @@ -7,7 +7,7 @@ import openpype.version from openpype.settings.lib import get_local_settings -from .execute import get_pype_execute_args +from .execute import get_openpype_execute_args from .local_settings import get_local_site_id from .python_module_tools import import_filepath @@ -71,7 +71,7 @@ def is_running_staging(): def get_pype_info(): """Information about currently used Pype process.""" - executable_args = get_pype_execute_args() + executable_args = get_openpype_execute_args() if is_running_from_build(): version_type = "build" else: diff --git a/openpype/modules/default_modules/ftrack/ftrack_server/event_server_cli.py b/openpype/modules/default_modules/ftrack/ftrack_server/event_server_cli.py index 1a76905b385..90ce7572427 100644 --- a/openpype/modules/default_modules/ftrack/ftrack_server/event_server_cli.py +++ b/openpype/modules/default_modules/ftrack/ftrack_server/event_server_cli.py @@ -14,7 +14,7 @@ import ftrack_api import pymongo from openpype.lib import ( - get_pype_execute_args, + get_openpype_execute_args, OpenPypeMongoConnection, get_openpype_version, get_build_version, @@ -136,7 +136,7 @@ def legacy_server(ftrack_url): if subproc is None: if subproc_failed_count < max_fail_count: - args = get_pype_execute_args("run", subproc_path) + args = get_openpype_execute_args("run", subproc_path) subproc = subprocess.Popen( args, stdout=subprocess.PIPE @@ -248,7 +248,7 @@ def on_exit(processor_thread, storer_thread, statuser_thread): ["Username", getpass.getuser()], ["Host Name", host_name], ["Host IP", socket.gethostbyname(host_name)], - ["OpenPype executable", get_pype_execute_args()[-1]], + ["OpenPype executable", get_openpype_execute_args()[-1]], ["OpenPype version", get_openpype_version() or "N/A"], ["OpenPype build version", get_build_version() or "N/A"] ] diff --git a/openpype/modules/default_modules/ftrack/ftrack_server/socket_thread.py b/openpype/modules/default_modules/ftrack/ftrack_server/socket_thread.py index eb8ec4d06c8..f49ca5557e1 100644 --- a/openpype/modules/default_modules/ftrack/ftrack_server/socket_thread.py +++ b/openpype/modules/default_modules/ftrack/ftrack_server/socket_thread.py @@ -6,7 +6,7 @@ import traceback import subprocess from openpype.api import Logger -from openpype.lib import get_pype_execute_args +from openpype.lib import get_openpype_execute_args class SocketThread(threading.Thread): @@ -59,7 +59,7 @@ def run(self): env = os.environ.copy() env["OPENPYPE_PROCESS_MONGO_ID"] = str(Logger.mongo_process_id) # OpenPype executable (with path to start script if not build) - args = get_pype_execute_args( + args = get_openpype_execute_args( # Add `run` command "run", self.filepath, diff --git a/openpype/modules/standalonepublish_action.py b/openpype/modules/standalonepublish_action.py index 9321a415a9a..ba53ce9b9ea 100644 --- a/openpype/modules/standalonepublish_action.py +++ b/openpype/modules/standalonepublish_action.py @@ -1,7 +1,7 @@ import os import platform import subprocess -from openpype.lib import get_pype_execute_args +from openpype.lib import get_openpype_execute_args from openpype.modules import OpenPypeModule from openpype_interfaces import ITrayAction @@ -35,7 +35,7 @@ def connect_with_modules(self, enabled_modules): self.publish_paths.extend(publish_paths) def run_standalone_publisher(self): - args = get_pype_execute_args("standalonepublisher") + args = get_openpype_execute_args("standalonepublisher") kwargs = {} if platform.system().lower() == "darwin": new_args = ["open", "-na", args.pop(0), "--args"] diff --git a/openpype/tools/standalonepublish/widgets/widget_components.py b/openpype/tools/standalonepublish/widgets/widget_components.py index 2ac54af4e3c..4d7f94f8256 100644 --- a/openpype/tools/standalonepublish/widgets/widget_components.py +++ b/openpype/tools/standalonepublish/widgets/widget_components.py @@ -10,7 +10,7 @@ from avalon import io from openpype.api import execute, Logger from openpype.lib import ( - get_pype_execute_args, + get_openpype_execute_args, apply_project_environments_value ) @@ -193,7 +193,7 @@ def cli_publish(data, publish_paths, gui=True): project_name = os.environ["AVALON_PROJECT"] env_copy = apply_project_environments_value(project_name, envcopy) - args = get_pype_execute_args("run", PUBLISH_SCRIPT_PATH) + args = get_openpype_execute_args("run", PUBLISH_SCRIPT_PATH) result = execute(args, env=envcopy) result = {} diff --git a/openpype/tools/tray/pype_tray.py b/openpype/tools/tray/pype_tray.py index 8c6a6d3266a..df0238c8485 100644 --- a/openpype/tools/tray/pype_tray.py +++ b/openpype/tools/tray/pype_tray.py @@ -14,7 +14,7 @@ resources, get_system_settings ) -from openpype.lib import get_pype_execute_args +from openpype.lib import get_openpype_execute_args from openpype.modules import TrayModulesManager from openpype import style from openpype.settings import ( @@ -208,10 +208,10 @@ def restart(self): First creates new process with same argument and close current tray. """ - args = get_pype_execute_args() + args = get_openpype_execute_args() # Create a copy of sys.argv additional_args = list(sys.argv) - # Check last argument from `get_pype_execute_args` + # Check last argument from `get_openpype_execute_args` # - when running from code it is the same as first from sys.argv if args[-1] == additional_args[0]: additional_args.pop(0) From 4ab54f4d735adf13f7a4db376000bc9454d2ff65 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 6 Jan 2022 16:27:41 +0100 Subject: [PATCH 395/492] added deprecation warning --- openpype/lib/execute.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/openpype/lib/execute.py b/openpype/lib/execute.py index 452a8fd4c0d..57cb01b4abc 100644 --- a/openpype/lib/execute.py +++ b/openpype/lib/execute.py @@ -148,6 +148,13 @@ def path_to_subprocess_arg(path): def get_pype_execute_args(*args): """Backwards compatible function for 'get_openpype_execute_args'.""" + import traceback + + log = Logger.get_logger("get_pype_execute_args") + stack = "\n".join(traceback.format_stack()) + log.warning(( + "Using deprecated function 'get_pype_execute_args'. Called from:\n{}" + ).format(stack)) return get_openpype_execute_args(*args) From 6de956dae2a876044bbbc77c7d023d53441e008d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 6 Jan 2022 16:28:03 +0100 Subject: [PATCH 396/492] implemented functions to run openpype subprocess with cleanin environments --- openpype/lib/__init__.py | 4 ++++ openpype/lib/execute.py | 43 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/openpype/lib/__init__.py b/openpype/lib/__init__.py index f721e0f5774..12e47a8961b 100644 --- a/openpype/lib/__init__.py +++ b/openpype/lib/__init__.py @@ -29,6 +29,8 @@ get_linux_launcher_args, execute, run_subprocess, + run_openpype_process, + clean_envs_for_openpype_process, path_to_subprocess_arg, CREATE_NO_WINDOW ) @@ -179,6 +181,8 @@ "get_linux_launcher_args", "execute", "run_subprocess", + "run_openpype_process", + "clean_envs_for_openpype_process", "path_to_subprocess_arg", "CREATE_NO_WINDOW", diff --git a/openpype/lib/execute.py b/openpype/lib/execute.py index 57cb01b4abc..16b98eefb48 100644 --- a/openpype/lib/execute.py +++ b/openpype/lib/execute.py @@ -138,6 +138,49 @@ def run_subprocess(*args, **kwargs): return full_output +def clean_envs_for_openpype_process(env=None): + """Modify environemnts that may affect OpenPype process. + + Main reason to implement this function is to pop PYTHONPATH which may be + affected by in-host environments. + """ + if env is None: + env = os.environ + return { + key: value + for key, value in env.items() + if key not in ("PYTHONPATH",) + } + + +def run_openpype_process(*args, **kwargs): + """Execute OpenPype process with passed arguments and wait. + + Wrapper for 'run_process' which prepends OpenPype executable arguments + before passed arguments and define environments if are not passed. + + Values from 'os.environ' are used for environments if are not passed. + They are cleaned using 'clean_envs_for_openpype_process' function. + + Example: + ``` + run_openpype_process(["run", ""]) + ``` + + Args: + *args (tuple): OpenPype cli arguments. + **kwargs (dict): Keyword arguments for for subprocess.Popen. + """ + args = get_openpype_execute_args(*args) + env = kwargs.pop("env", None) + # Keep env untouched if are passed and not empty + if not env: + # Skip envs that can affect OpenPype process + # - fill more if you find more + env = clean_envs_for_openpype_process(os.environ) + return run_subprocess(args, env=env, **kwargs) + + def path_to_subprocess_arg(path): """Prepare path for subprocess arguments. From ac6280f959cc50f980bcc37f917bc86c43d36825 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 6 Jan 2022 16:28:43 +0100 Subject: [PATCH 397/492] use run_openpype_process in extract burnin --- openpype/plugins/publish/extract_burnin.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/openpype/plugins/publish/extract_burnin.py b/openpype/plugins/publish/extract_burnin.py index df7dc47e177..1cb8608a563 100644 --- a/openpype/plugins/publish/extract_burnin.py +++ b/openpype/plugins/publish/extract_burnin.py @@ -13,7 +13,7 @@ import openpype import openpype.api from openpype.lib import ( - get_pype_execute_args, + run_openpype_process, get_transcode_temp_directory, convert_for_ffmpeg, @@ -168,9 +168,8 @@ def main_process(self, instance): anatomy = instance.context.data["anatomy"] scriptpath = self.burnin_script_path() - # Executable args that will execute the script - # [pype executable, *pype script, "run"] - executable_args = get_pype_execute_args("run", scriptpath) + # Args that will execute the script + executable_args = ["run", scriptpath] burnins_per_repres = self._get_burnins_per_representations( instance, burnin_defs ) @@ -313,7 +312,7 @@ def main_process(self, instance): if platform.system().lower() == "windows": process_kwargs["creationflags"] = CREATE_NO_WINDOW - openpype.api.run_subprocess(args, **process_kwargs) + run_openpype_process(args, **process_kwargs) # Remove the temporary json os.remove(temporary_json_filepath) From ae74e0a79569308fa6dc6e602dc0b6d0d3e3c323 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 6 Jan 2022 16:37:02 +0100 Subject: [PATCH 398/492] flame: stylize creator gui --- openpype/hosts/flame/api/style.css | 51 +++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/flame/api/style.css b/openpype/hosts/flame/api/style.css index b64c391c6e9..21e4750513d 100644 --- a/openpype/hosts/flame/api/style.css +++ b/openpype/hosts/flame/api/style.css @@ -1,5 +1,5 @@ QWidget { - font-size: 13px; + font: 14px "Discreet"; } QSpinBox { @@ -8,13 +8,46 @@ QSpinBox { } QLineEdit { - padding: 2; - min-width: 15em; + color: #9a9a9a; + background-color: #373e47; + selection-color: #262626; + selection-background-color: #b8b1a7; + font: 14px "Discreet" +} +QLineEdit:focus { + background-color: #474e58 +} + +QLineEdit:disabled { + color: #6a6a6a; + background-color: #373737 +} + +QPushButton { + color: #9a9a9a; + background-color: #424142; + border-top: 1px inset #555555; + border-bottom: 1px inset black; + font: 14px "Discreet" +} + +QPushButton:pressed { + color: #d9d9d9; + background-color: #4f4f4f; + border-top: 1px inset #666666; + font: italic +} + +QPushButton:disabled { + color: #747474; + background-color: #353535; + border-top: 1px solid #444444; + border-bottom: 1px solid #242424 } QVBoxLayout { min-width: 15em; - background-color: #201f1f; + background-color: #313131 } QComboBox { @@ -23,4 +56,14 @@ QComboBox { #sectionContent { background-color: #2E2D2D; +} + +QLabel { + color: #9a9a9a; + border-bottom: 1px inset #282828; + font: 14px "Discreet" +} + +QLabel:disabled { + color: #6a6a6a } \ No newline at end of file From ed5da3e0b04b28019e2b5c192364ced71d9a2c27 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 6 Jan 2022 16:39:59 +0100 Subject: [PATCH 399/492] expect that get_remote_versions may return None --- openpype/settings/entities/op_version_entity.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openpype/settings/entities/op_version_entity.py b/openpype/settings/entities/op_version_entity.py index 6184f7640a8..782d65a446d 100644 --- a/openpype/settings/entities/op_version_entity.py +++ b/openpype/settings/entities/op_version_entity.py @@ -72,6 +72,8 @@ class ProductionVersionsInputEntity(OpenPypeVersionInput): def _get_openpype_versions(self): versions = get_remote_versions(staging=False, production=True) + if versions is None: + return [] versions.append(get_installed_version()) return sorted(versions) @@ -82,4 +84,6 @@ class StagingVersionsInputEntity(OpenPypeVersionInput): def _get_openpype_versions(self): versions = get_remote_versions(staging=True, production=False) + if versions is None: + return [] return sorted(versions) From 09214e175b9674fb465517eaf5e4092701e09f6d Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 6 Jan 2022 16:59:02 +0100 Subject: [PATCH 400/492] Flame: adding openpype style to creator gui --- openpype/hosts/flame/api/plugin.py | 15 +------ openpype/hosts/flame/api/style.css | 69 ------------------------------ 2 files changed, 2 insertions(+), 82 deletions(-) delete mode 100644 openpype/hosts/flame/api/style.css diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index 5e47ce3a688..b4fc75fe00f 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -2,23 +2,13 @@ import os from Qt import QtWidgets, QtCore import openpype.api as openpype +from openpype import style import openpype.hosts.flame as opflame from . import lib, pipeline from copy import deepcopy log = openpype.Logger().get_logger(__name__) -# Creator plugin functions -def load_stylesheet(): - path = os.path.join(os.path.dirname(__file__), "style.css") - if not os.path.exists(path): - log.warning("Unable to load stylesheet, file not found in resources") - return "" - - with open(path, "r") as file_stream: - stylesheet = file_stream.read() - return stylesheet - class CreatorWidget(QtWidgets.QDialog): @@ -93,8 +83,7 @@ def __init__(self, name, info, ui_inputs, parent=None): ok_btn.clicked.connect(self._on_ok_clicked) cancel_btn.clicked.connect(self._on_cancel_clicked) - stylesheet = load_stylesheet() - self.setStyleSheet(stylesheet) + self.setStyleSheet(style.load_stylesheet()) def _on_ok_clicked(self): self.result = self.value(self.items) diff --git a/openpype/hosts/flame/api/style.css b/openpype/hosts/flame/api/style.css deleted file mode 100644 index 21e4750513d..00000000000 --- a/openpype/hosts/flame/api/style.css +++ /dev/null @@ -1,69 +0,0 @@ -QWidget { - font: 14px "Discreet"; -} - -QSpinBox { - padding: 2; - max-width: 8em; -} - -QLineEdit { - color: #9a9a9a; - background-color: #373e47; - selection-color: #262626; - selection-background-color: #b8b1a7; - font: 14px "Discreet" -} -QLineEdit:focus { - background-color: #474e58 -} - -QLineEdit:disabled { - color: #6a6a6a; - background-color: #373737 -} - -QPushButton { - color: #9a9a9a; - background-color: #424142; - border-top: 1px inset #555555; - border-bottom: 1px inset black; - font: 14px "Discreet" -} - -QPushButton:pressed { - color: #d9d9d9; - background-color: #4f4f4f; - border-top: 1px inset #666666; - font: italic -} - -QPushButton:disabled { - color: #747474; - background-color: #353535; - border-top: 1px solid #444444; - border-bottom: 1px solid #242424 -} - -QVBoxLayout { - min-width: 15em; - background-color: #313131 -} - -QComboBox { - min-width: 8em; -} - -#sectionContent { - background-color: #2E2D2D; -} - -QLabel { - color: #9a9a9a; - border-bottom: 1px inset #282828; - font: 14px "Discreet" -} - -QLabel:disabled { - color: #6a6a6a -} \ No newline at end of file From 16d920028ce4fe9492be653ffdedc8b26756310d Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 6 Jan 2022 17:04:24 +0100 Subject: [PATCH 401/492] Flame: adding closing event to creator gui --- openpype/hosts/flame/api/plugin.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index b4fc75fe00f..68bdbbe5103 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -93,6 +93,10 @@ def _on_cancel_clicked(self): self.result = None self.close() + def closeEvent(self, event): + self.result = None + event.accept() + def value(self, data, new_data=None): new_data = new_data or dict() for k, v in data.items(): From bbbfef4ca793765e0930f7f95af93f655303cbec Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 6 Jan 2022 17:32:54 +0100 Subject: [PATCH 402/492] changed action/event paths to multiplatform and multipath inputs and changed labels --- .../defaults/system_settings/modules.json | 12 ++++++++++-- .../module_settings/schema_ftrack.json | 18 +++++++++++------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/openpype/settings/defaults/system_settings/modules.json b/openpype/settings/defaults/system_settings/modules.json index f0caa153dee..b31dd6856cf 100644 --- a/openpype/settings/defaults/system_settings/modules.json +++ b/openpype/settings/defaults/system_settings/modules.json @@ -15,8 +15,16 @@ "ftrack": { "enabled": true, "ftrack_server": "", - "ftrack_actions_path": [], - "ftrack_events_path": [], + "ftrack_actions_path": { + "windows": [], + "darwin": [], + "linux": [] + }, + "ftrack_events_path": { + "windows": [], + "darwin": [], + "linux": [] + }, "intent": { "items": { "-": "-", diff --git a/openpype/settings/entities/schemas/system_schema/module_settings/schema_ftrack.json b/openpype/settings/entities/schemas/system_schema/module_settings/schema_ftrack.json index 5f659522c30..654ddf29389 100644 --- a/openpype/settings/entities/schemas/system_schema/module_settings/schema_ftrack.json +++ b/openpype/settings/entities/schemas/system_schema/module_settings/schema_ftrack.json @@ -21,19 +21,23 @@ }, { "type": "label", - "label": "Additional Ftrack paths" + "label": "Additional Ftrack event handlers paths" }, { - "type": "list", + "type": "path", "key": "ftrack_actions_path", - "label": "Action paths", - "object_type": "text" + "label": "User paths", + "use_label_wrap": true, + "multipath": true, + "multiplatform": true }, { - "type": "list", + "type": "path", "key": "ftrack_events_path", - "label": "Event paths", - "object_type": "text" + "label": "Server paths", + "use_label_wrap": true, + "multipath": true, + "multiplatform": true }, { "type": "separator" From 5ec147b336afe2499d428cbc81251b629d6e5ee5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 6 Jan 2022 17:42:36 +0100 Subject: [PATCH 403/492] ftrack module expect new type of path settings --- .../default_modules/ftrack/ftrack_module.py | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/openpype/modules/default_modules/ftrack/ftrack_module.py b/openpype/modules/default_modules/ftrack/ftrack_module.py index 8a7525d65b1..e24869fd59a 100644 --- a/openpype/modules/default_modules/ftrack/ftrack_module.py +++ b/openpype/modules/default_modules/ftrack/ftrack_module.py @@ -1,6 +1,7 @@ import os import json import collections +import platform import click @@ -42,18 +43,28 @@ def initialize(self, settings): self.ftrack_url = ftrack_url current_dir = os.path.dirname(os.path.abspath(__file__)) + low_platform = platform.system().lower() + server_event_handlers_paths = [ os.path.join(current_dir, "event_handlers_server") ] - server_event_handlers_paths.extend( - ftrack_settings["ftrack_events_path"] - ) + settings_server_paths = ftrack_settings["ftrack_events_path"] + if isinstance(settings_server_paths, dict): + settings_server_paths = settings_server_paths[low_platform] + + for path in settings_server_paths: + server_event_handlers_paths.append(path) + user_event_handlers_paths = [ os.path.join(current_dir, "event_handlers_user") ] - user_event_handlers_paths.extend( - ftrack_settings["ftrack_actions_path"] - ) + settings_action_paths = ftrack_settings["ftrack_actions_path"] + if isinstance(settings_action_paths, dict): + settings_action_paths = settings_action_paths[low_platform] + + for path in settings_action_paths: + user_event_handlers_paths.append(path) + # Prepare attribute self.server_event_handlers_paths = server_event_handlers_paths self.user_event_handlers_paths = user_event_handlers_paths From dd80f331f70ab7d51a32f4fab67871bb69e497ee Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 6 Jan 2022 17:42:51 +0100 Subject: [PATCH 404/492] paths to event handler are tried to format with environments --- .../modules/default_modules/ftrack/ftrack_module.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/openpype/modules/default_modules/ftrack/ftrack_module.py b/openpype/modules/default_modules/ftrack/ftrack_module.py index e24869fd59a..1b41159069a 100644 --- a/openpype/modules/default_modules/ftrack/ftrack_module.py +++ b/openpype/modules/default_modules/ftrack/ftrack_module.py @@ -53,6 +53,11 @@ def initialize(self, settings): settings_server_paths = settings_server_paths[low_platform] for path in settings_server_paths: + # Try to format path with environments + try: + path = path.format(**os.environ) + except BaseException: + pass server_event_handlers_paths.append(path) user_event_handlers_paths = [ @@ -63,6 +68,11 @@ def initialize(self, settings): settings_action_paths = settings_action_paths[low_platform] for path in settings_action_paths: + # Try to format path with environments + try: + path = path.format(**os.environ) + except BaseException: + pass user_event_handlers_paths.append(path) # Prepare attribute From 85d973555b01d28f1b39f58f8f2bc93d2d229606 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 6 Jan 2022 17:44:41 +0100 Subject: [PATCH 405/492] try format the paths in ftrack event server (more dynamic) --- .../default_modules/ftrack/ftrack_module.py | 20 ++++--------------- .../ftrack/ftrack_server/ftrack_server.py | 6 ++++++ 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/openpype/modules/default_modules/ftrack/ftrack_module.py b/openpype/modules/default_modules/ftrack/ftrack_module.py index 1b41159069a..38ec02749a9 100644 --- a/openpype/modules/default_modules/ftrack/ftrack_module.py +++ b/openpype/modules/default_modules/ftrack/ftrack_module.py @@ -45,35 +45,23 @@ def initialize(self, settings): current_dir = os.path.dirname(os.path.abspath(__file__)) low_platform = platform.system().lower() + # Server event handler paths server_event_handlers_paths = [ os.path.join(current_dir, "event_handlers_server") ] settings_server_paths = ftrack_settings["ftrack_events_path"] if isinstance(settings_server_paths, dict): settings_server_paths = settings_server_paths[low_platform] + server_event_handlers_paths.extend(settings_server_paths) - for path in settings_server_paths: - # Try to format path with environments - try: - path = path.format(**os.environ) - except BaseException: - pass - server_event_handlers_paths.append(path) - + # User event handler paths user_event_handlers_paths = [ os.path.join(current_dir, "event_handlers_user") ] settings_action_paths = ftrack_settings["ftrack_actions_path"] if isinstance(settings_action_paths, dict): settings_action_paths = settings_action_paths[low_platform] - - for path in settings_action_paths: - # Try to format path with environments - try: - path = path.format(**os.environ) - except BaseException: - pass - user_event_handlers_paths.append(path) + user_event_handlers_paths.extend(settings_action_paths) # Prepare attribute self.server_event_handlers_paths = server_event_handlers_paths diff --git a/openpype/modules/default_modules/ftrack/ftrack_server/ftrack_server.py b/openpype/modules/default_modules/ftrack/ftrack_server/ftrack_server.py index bd67fba3d6e..8944591b714 100644 --- a/openpype/modules/default_modules/ftrack/ftrack_server/ftrack_server.py +++ b/openpype/modules/default_modules/ftrack/ftrack_server/ftrack_server.py @@ -63,6 +63,12 @@ def set_files(self, paths): # Iterate all paths register_functions = [] for path in paths: + # Try to format path with environments + try: + path = path.format(**os.environ) + except BaseException: + pass + # Get all modules with functions modules, crashed = modules_from_path(path) for filepath, exc_info in crashed: From 9de27a8b04b1b16dbf0b1ca32b308b0d8ef46122 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 7 Jan 2022 12:04:15 +0100 Subject: [PATCH 406/492] OP-2204 - added new setting for upload review in Slack notification --- .../schemas/projects_schema/schema_project_slack.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_slack.json b/openpype/settings/entities/schemas/projects_schema/schema_project_slack.json index 9ca4e443bdb..4e82c991e71 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_slack.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_slack.json @@ -91,6 +91,11 @@ "key": "upload_thumbnail", "label": "Upload thumbnail" }, + { + "type": "boolean", + "key": "upload_review", + "label": "Upload review" + }, { "type": "text", "multiline": true, From 2229e19caa5c3fd90ce4b8ab1516c56bbd64d3ee Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 7 Jan 2022 12:06:33 +0100 Subject: [PATCH 407/492] OP-2204 - added possibility to upload or add link to review to Slack notification --- .../plugins/publish/integrate_slack_api.py | 134 ++++++++++++------ 1 file changed, 88 insertions(+), 46 deletions(-) diff --git a/openpype/modules/slack/plugins/publish/integrate_slack_api.py b/openpype/modules/slack/plugins/publish/integrate_slack_api.py index 7b81d3c364b..b9f4b9d81fa 100644 --- a/openpype/modules/slack/plugins/publish/integrate_slack_api.py +++ b/openpype/modules/slack/plugins/publish/integrate_slack_api.py @@ -14,6 +14,8 @@ class IntegrateSlackAPI(pyblish.api.InstancePlugin): Project settings > Slack > Publish plugins > Notification to Slack. If instance contains 'thumbnail' it uploads it. Bot must be present in the target channel. + If instance contains 'review' it could upload (if configured) or place + link with {review_link} placeholder. Message template can contain {} placeholders from anatomyData. """ order = pyblish.api.IntegratorOrder + 0.499 @@ -23,44 +25,68 @@ class IntegrateSlackAPI(pyblish.api.InstancePlugin): optional = True def process(self, instance): - published_path = self._get_thumbnail_path(instance) + thumbnail_path = self._get_thumbnail_path(instance) + review_path = self._get_review_path(instance) + publish_files = set() for message_profile in instance.data["slack_channel_message_profiles"]: message = self._get_filled_message(message_profile["message"], - instance) + instance, + review_path) if not message: return + if message_profile["upload_thumbnail"] and thumbnail_path: + publish_files.add(thumbnail_path) + + if message_profile["upload_review"] and review_path: + publish_files.add(review_path) + for channel in message_profile["channels"]: if six.PY2: self._python2_call(instance.data["slack_token"], channel, message, - published_path, - message_profile["upload_thumbnail"]) + publish_files) else: self._python3_call(instance.data["slack_token"], channel, message, - published_path, - message_profile["upload_thumbnail"]) + publish_files) + + def _get_filled_message(self, message_templ, instance, review_path=None): + """Use message_templ and data from instance to get message content. - def _get_filled_message(self, message_templ, instance): - """Use message_templ and data from instance to get message content.""" + Reviews might be large, so allow only adding link to message instead of + uploading only. + """ fill_data = copy.deepcopy(instance.context.data["anatomyData"]) - fill_pairs = ( + fill_pairs = [ ("asset", instance.data.get("asset", fill_data.get("asset"))), ("subset", instance.data.get("subset", fill_data.get("subset"))), - ("task", instance.data.get("task", fill_data.get("task"))), ("username", instance.data.get("username", fill_data.get("username"))), ("app", instance.data.get("app", fill_data.get("app"))), ("family", instance.data.get("family", fill_data.get("family"))), ("version", str(instance.data.get("version", fill_data.get("version")))) - ) - + ] + if review_path: + fill_pairs.append(("review_link", review_path)) + + task_on_instance = instance.data.get("task") + task_on_anatomy = fill_data.get("task") + if task_on_instance: + fill_pairs.append(("task[name]", task_on_instance.get("type"))) + fill_pairs.append(("task[name]", task_on_instance.get("name"))) + fill_pairs.append(("task[short]", task_on_instance.get("short"))) + elif task_on_anatomy: + fill_pairs.append(("task[name]", task_on_anatomy.get("type"))) + fill_pairs.append(("task[name]", task_on_anatomy.get("name"))) + fill_pairs.append(("task[short]", task_on_anatomy.get("short"))) + + self.log.debug("fill_pairs ::{}".format(fill_pairs)) multiple_case_variants = prepare_template_data(fill_pairs) fill_data.update(multiple_case_variants) @@ -79,39 +105,51 @@ def _get_thumbnail_path(self, instance): published_path = None for repre in instance.data['representations']: if repre.get('thumbnail') or "thumbnail" in repre.get('tags', []): - repre_files = repre["files"] - if isinstance(repre_files, (tuple, list, set)): - filename = repre_files[0] - else: - filename = repre_files - - published_path = os.path.join( - repre['stagingDir'], filename - ) + if os.path.exists(repre["published_path"]): + published_path = repre["published_path"] break return published_path + def _get_review_path(self, instance): + """Returns abs url for review if present in instance repres""" + published_path = None + for repre in instance.data['representations']: + tags = repre.get('tags', []) + if (repre.get("review") + or "review" in tags + or "burnin" in tags): + if os.path.exists(repre["published_path"]): + published_path = repre["published_path"] + if "burnin" in tags: # burnin has precedence if exists + break + return published_path + def _python2_call(self, token, channel, message, - published_path, upload_thumbnail): + publish_files): from slackclient import SlackClient try: client = SlackClient(token) - if upload_thumbnail and \ - published_path and os.path.exists(published_path): - with open(published_path, 'rb') as pf: + for p_file in publish_files: + attachment_str = "\n\n Attachment links: \n" + with open(p_file, 'rb') as pf: response = client.api_call( "files.upload", channels=channel, - initial_comment=message, file=pf, - title=os.path.basename(published_path) + title=os.path.basename(p_file), ) - else: - response = client.api_call( - "chat.postMessage", - channel=channel, - text=message - ) + attachment_str += "\n<{}|{}>".format( + response["file"]["permalink"], + os.path.basename(p_file)) + + if publish_files: + message += attachment_str + + response = client.api_call( + "chat.postMessage", + channel=channel, + text=message + ) if response.get("error"): error_str = self._enrich_error(str(response.get("error")), @@ -123,23 +161,27 @@ def _python2_call(self, token, channel, message, self.log.warning("Error happened: {}".format(error_str)) def _python3_call(self, token, channel, message, - published_path, upload_thumbnail): + publish_files): from slack_sdk import WebClient from slack_sdk.errors import SlackApiError try: client = WebClient(token=token) - if upload_thumbnail and \ - published_path and os.path.exists(published_path): - _ = client.files_upload( - channels=channel, - initial_comment=message, - file=published_path, - ) - else: - _ = client.chat_postMessage( - channel=channel, - text=message - ) + attachment_str = "\n\n Attachment links: \n" + for published_file in publish_files: + response = client.files_upload( + file=published_file, + filename=os.path.basename(published_file)) + attachment_str += "\n<{}|{}>".format( + response["file"]["permalink"], + os.path.basename(published_file)) + + if publish_files: + message += attachment_str + + _ = client.chat_postMessage( + channel=channel, + text=message + ) except SlackApiError as e: # You will get a SlackApiError if "ok" is False error_str = self._enrich_error(str(e.response["error"]), channel) From bdd7d159bac52d598f2eaa674813664402594e0d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 7 Jan 2022 12:07:35 +0100 Subject: [PATCH 408/492] OP-2204 - added documentation to Slack section --- website/docs/module_slack.md | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/website/docs/module_slack.md b/website/docs/module_slack.md index f71fcc2bb7f..10d5e58eac3 100644 --- a/website/docs/module_slack.md +++ b/website/docs/module_slack.md @@ -61,6 +61,18 @@ Integration can upload 'thumbnail' file (if present in an instance), for that bo manually added to target channel by Slack admin! (In target channel write: ```/invite @OpenPypeNotifier``) +#### Upload review +Integration can upload 'review' file (if present in an instance), for that bot must be +manually added to target channel by Slack admin! +(In target channel write: ```/invite @OpenPypeNotifier``) + +Burnin version (usually .mp4) is preferred if present. + +Please be sure that this configuration is viable for your use case. In case of uploading large reviews to Slack, +all publishes will be slowed down and you might hit a file limit on Slack pretty soon (it is 5GB for Free version of Slack, any file cannot be bigger than 1GB). +You might try to add `{review_link}` to message content. This link might help users to find review easier on their machines. +(It won't show a playable preview though!) + #### Message Message content can use Templating (see [Available template keys](admin_settings_project_anatomy#available-template-keys)). @@ -69,8 +81,22 @@ Few keys also have Capitalized and UPPERCASE format. Values will be modified acc **Available keys:** - asset - subset -- task +- task\[name\] +- task\[type\] +- task\[short\] - username - app - family - version +- review_link + +##### Message example +``` +{Subset} was published for {ASSET} in {task[name]} task. + +Here you can find review {review_link} +``` + +#### Message retention +Currently no purging of old messages is implemented in Openpype. Admins of Slack should set their own retention of messages and files per channel. +(see https://slack.com/help/articles/203457187-Customize-message-and-file-retention-policies) From 169b896ef40303b375003e5a2b3d63701894954f Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 7 Jan 2022 14:26:06 +0100 Subject: [PATCH 409/492] flame: refactory api calls --- openpype/hosts/flame/__init__.py | 121 ------------------ openpype/hosts/flame/api/__init__.py | 114 +++++++++++++++++ openpype/hosts/flame/api/constants.py | 24 ++++ openpype/hosts/flame/api/lib.py | 54 ++++---- openpype/hosts/flame/api/menu.py | 10 +- openpype/hosts/flame/api/pipeline.py | 11 +- openpype/hosts/flame/api/plugin.py | 28 ++-- .../hosts/flame/api/scripts/wiretap_com.py | 4 +- .../api/utility_scripts/openpype_in_flame.py | 45 +++---- openpype/hosts/flame/api/utils.py | 2 +- openpype/hosts/flame/api/workio.py | 2 +- openpype/hosts/flame/hooks/pre_flame_setup.py | 3 +- openpype/hosts/flame/otio/flame_export.py | 2 +- .../flame/plugins/create/create_shot_clip.py | 16 +-- .../plugins/publish/collect_test_selection.py | 18 ++- 15 files changed, 239 insertions(+), 215 deletions(-) create mode 100644 openpype/hosts/flame/api/constants.py diff --git a/openpype/hosts/flame/__init__.py b/openpype/hosts/flame/__init__.py index da42b313aab..02befa76e27 100644 --- a/openpype/hosts/flame/__init__.py +++ b/openpype/hosts/flame/__init__.py @@ -1,126 +1,5 @@ -from .api.utils import ( - setup -) - -from .api.pipeline import ( - install, - uninstall, - ls, - containerise, - update_container, - remove_instance, - list_instances, - imprint, - maintained_selection -) - -from .api.lib import ( - FlameAppFramework, - maintain_current_timeline, - get_project_manager, - get_current_project, - get_current_sequence, - create_bin, - create_segment_data_marker, - get_segment_data_marker, - set_segment_data_marker, - set_publish_attribute, - get_publish_attribute, - get_sequence_segments, - maintained_segment_selection, - reset_segment_selection, - get_segment_attributes -) - -from .api.menu import ( - FlameMenuProjectConnect, - FlameMenuTimeline -) - -from .api.workio import ( - open_file, - save_file, - current_file, - has_unsaved_changes, - file_extensions, - work_root -) - import os HOST_DIR = os.path.dirname( os.path.abspath(__file__) ) -API_DIR = os.path.join(HOST_DIR, "api") -PLUGINS_DIR = os.path.join(HOST_DIR, "plugins") -PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish") -LOAD_PATH = os.path.join(PLUGINS_DIR, "load") -CREATE_PATH = os.path.join(PLUGINS_DIR, "create") -INVENTORY_PATH = os.path.join(PLUGINS_DIR, "inventory") - -app_framework = None -apps = [] -selection = None - - -__all__ = [ - "HOST_DIR", - "API_DIR", - "PLUGINS_DIR", - "PUBLISH_PATH", - "LOAD_PATH", - "CREATE_PATH", - "INVENTORY_PATH", - "INVENTORY_PATH", - - "app_framework", - "apps", - "selection", - - # pipeline - "install", - "uninstall", - "ls", - "containerise", - "update_container", - "reload_pipeline", - "maintained_selection", - "remove_instance", - "list_instances", - "imprint", - "maintained_selection", - - # utils - "setup", - - # lib - "FlameAppFramework", - "maintain_current_timeline", - "get_project_manager", - "get_current_project", - "get_current_sequence", - "create_bin", - "create_segment_data_marker", - "get_segment_data_marker", - "set_segment_data_marker", - "set_publish_attribute", - "get_publish_attribute", - "get_sequence_segments", - "maintained_segment_selection", - "reset_segment_selection", - "get_segment_attributes" - - # menu - "FlameMenuProjectConnect", - "FlameMenuTimeline", - - # plugin - - # workio - "open_file", - "save_file", - "current_file", - "has_unsaved_changes", - "file_extensions", - "work_root" -] diff --git a/openpype/hosts/flame/api/__init__.py b/openpype/hosts/flame/api/__init__.py index 50a6b3f0982..c8660aafc4d 100644 --- a/openpype/hosts/flame/api/__init__.py +++ b/openpype/hosts/flame/api/__init__.py @@ -1,3 +1,117 @@ """ OpenPype Autodesk Flame api """ +from .constants import ( + COLOR_MAP, + MARKER_NAME, + MARKER_COLOR, + MARKER_DURATION, + MARKER_PUBLISH_DEFAULT +) +from .lib import ( + CTX, + FlameAppFramework, + maintain_current_timeline, + get_project_manager, + get_current_project, + get_current_sequence, + create_bin, + create_segment_data_marker, + get_segment_data_marker, + set_segment_data_marker, + set_publish_attribute, + get_publish_attribute, + get_sequence_segments, + maintained_segment_selection, + reset_segment_selection, + get_segment_attributes +) +from .utils import ( + setup +) +from .pipeline import ( + install, + uninstall, + ls, + containerise, + update_container, + remove_instance, + list_instances, + imprint, + maintained_selection +) +from .menu import ( + FlameMenuProjectConnect, + FlameMenuTimeline +) +from .plugin import ( + Creator, + PublishableClip +) +from .workio import ( + open_file, + save_file, + current_file, + has_unsaved_changes, + file_extensions, + work_root +) + +__all__ = [ + # constants + "COLOR_MAP", + "MARKER_NAME", + "MARKER_COLOR", + "MARKER_DURATION", + "MARKER_PUBLISH_DEFAULT", + + # lib + "CTX", + "FlameAppFramework", + "maintain_current_timeline", + "get_project_manager", + "get_current_project", + "get_current_sequence", + "create_bin", + "create_segment_data_marker", + "get_segment_data_marker", + "set_segment_data_marker", + "set_publish_attribute", + "get_publish_attribute", + "get_sequence_segments", + "maintained_segment_selection", + "reset_segment_selection", + "get_segment_attributes", + + # pipeline + "install", + "uninstall", + "ls", + "containerise", + "update_container", + "reload_pipeline", + "maintained_selection", + "remove_instance", + "list_instances", + "imprint", + "maintained_selection", + + # utils + "setup", + + # menu + "FlameMenuProjectConnect", + "FlameMenuTimeline", + + # plugin + "Creator", + "PublishableClip", + + # workio + "open_file", + "save_file", + "current_file", + "has_unsaved_changes", + "file_extensions", + "work_root" +] diff --git a/openpype/hosts/flame/api/constants.py b/openpype/hosts/flame/api/constants.py new file mode 100644 index 00000000000..1833031e139 --- /dev/null +++ b/openpype/hosts/flame/api/constants.py @@ -0,0 +1,24 @@ + +""" +OpenPype Flame api constances +""" +# OpenPype marker workflow variables +MARKER_NAME = "OpenPypeData" +MARKER_DURATION = 0 +MARKER_COLOR = "cyan" +MARKER_PUBLISH_DEFAULT = False + +# OpenPype color definitions +COLOR_MAP = { + "red": (1.0, 0.0, 0.0), + "orange": (1.0, 0.5, 0.0), + "yellow": (1.0, 1.0, 0.0), + "pink": (1.0, 0.5, 1.0), + "white": (1.0, 1.0, 1.0), + "green": (0.0, 1.0, 0.0), + "cyan": (0.0, 1.0, 1.0), + "blue": (0.0, 0.0, 1.0), + "purple": (0.5, 0.0, 0.5), + "magenta": (0.5, 0.0, 1.0), + "black": (0.0, 0.0, 0.0) +} diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index e5642dd6f93..ccc664ce63e 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -5,30 +5,24 @@ import pickle import contextlib from pprint import pformat - +from .constants import ( + MARKER_COLOR, + MARKER_DURATION, + MARKER_NAME, + COLOR_MAP, + MARKER_PUBLISH_DEFAULT +) from openpype.api import Logger -log = Logger().get_logger(__name__) - -class ctx: - # OpenPype marker workflow variables - marker_name = "OpenPypeData" - marker_duration = 0 - marker_color = "cyan" - publish_default = False - color_map = { - "red": (1.0, 0.0, 0.0), - "orange": (1.0, 0.5, 0.0), - "yellow": (1.0, 1.0, 0.0), - "pink": (1.0, 0.5, 1.0), - "white": (1.0, 1.0, 1.0), - "green": (0.0, 1.0, 0.0), - "cyan": (0.0, 1.0, 1.0), - "blue": (0.0, 0.0, 1.0), - "purple": (0.5, 0.0, 0.5), - "magenta": (0.5, 0.0, 1.0), - "black": (0.0, 0.0, 0.0) -} +log = Logger.get_logger(__name__) + + +class CTX: + # singleton used for passing data between api modules + app_framework = None + apps = [] + selection = None + @contextlib.contextmanager def io_preferences_file(klass, filepath, write=False): @@ -379,7 +373,8 @@ def get_segment_data_marker(segment, with_marker=None): color = marker.colour.get_value() name = marker.name.get_value() - if name == ctx.marker_name and color == ctx.color_map[ctx.marker_color]: + if (name == MARKER_NAME) and ( + color == COLOR_MAP[MARKER_COLOR]): if not with_marker: return json.loads(comment) else: @@ -443,8 +438,8 @@ def get_publish_attribute(segment): tag_data = get_segment_data_marker(segment) if not tag_data: - set_publish_attribute(segment, ctx.publish_default) - return ctx.publish_default + set_publish_attribute(segment, MARKER_PUBLISH_DEFAULT) + return MARKER_PUBLISH_DEFAULT return tag_data["publish"] @@ -465,14 +460,15 @@ def create_segment_data_marker(segment): # create marker marker = segment.create_marker(start_frame) # set marker name - marker.name = ctx.marker_name + marker.name = MARKER_NAME # set duration - marker.duration = ctx.marker_duration + marker.duration = MARKER_DURATION # set colour - marker.colour = ctx.color_map[ctx.marker_color] # Red + marker.colour = COLOR_MAP[MARKER_COLOR] # Red return marker + def get_sequence_segments(sequence, selected=False): segments = [] # loop versions in sequence @@ -485,7 +481,7 @@ def get_sequence_segments(sequence, selected=False): # loop all segment in remaining tracks for segment in track.segments: # ignore all segments not selected - if segment.selected != True and selected == True: + if segment.selected is not True and selected is True: continue # add it to original selection segments.append(segment) diff --git a/openpype/hosts/flame/api/menu.py b/openpype/hosts/flame/api/menu.py index fef6dbfa355..642c40a7df4 100644 --- a/openpype/hosts/flame/api/menu.py +++ b/openpype/hosts/flame/api/menu.py @@ -1,7 +1,7 @@ import os from Qt import QtWidgets from copy import deepcopy - +from pprint import pformat from openpype.tools.utils.host_tools import HostToolsHelper menu_group_name = 'OpenPype' @@ -26,9 +26,11 @@ def callback_selection(selection, function): - import openpype.hosts.flame as opflame - opflame.selection = selection - print(opflame.selection) + import openpype.hosts.flame.api as opfapi + opfapi.CTX.selection = selection + print("Hook Selection: \n\t{}".format( + pformat({type(item): item.name for item in CTX.selection}) + )) function() diff --git a/openpype/hosts/flame/api/pipeline.py b/openpype/hosts/flame/api/pipeline.py index ee0e12584a1..5333a072101 100644 --- a/openpype/hosts/flame/api/pipeline.py +++ b/openpype/hosts/flame/api/pipeline.py @@ -1,6 +1,7 @@ """ Basic avalon integration """ +import os import contextlib from avalon import api as avalon from pyblish import api as pyblish @@ -11,10 +12,18 @@ maintained_segment_selection, get_current_sequence ) +from .. import HOST_DIR + +API_DIR = os.path.join(HOST_DIR, "api") +PLUGINS_DIR = os.path.join(HOST_DIR, "plugins") +PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish") +LOAD_PATH = os.path.join(PLUGINS_DIR, "load") +CREATE_PATH = os.path.join(PLUGINS_DIR, "create") +INVENTORY_PATH = os.path.join(PLUGINS_DIR, "inventory") AVALON_CONTAINERS = "AVALON_CONTAINERS" -log = Logger().get_logger(__name__) +log = Logger.get_logger(__name__) def install(): diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index 68bdbbe5103..4f71f9424e7 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -1,13 +1,17 @@ import re -import os from Qt import QtWidgets, QtCore import openpype.api as openpype from openpype import style -import openpype.hosts.flame as opflame -from . import lib, pipeline +from . import selection as opfapi_selection +from . import ( + lib as flib, + pipeline as fpipeline, + constants +) + from copy import deepcopy -log = openpype.Logger().get_logger(__name__) +log = openpype.Logger.get_logger(__name__) class CreatorWidget(QtWidgets.QDialog): @@ -283,7 +287,7 @@ def __init__(self, height, *args, **kwargs): class Creator(openpype.Creator): """Creator class wrapper """ - clip_color = lib.ctx.color_map["purple"] + clip_color = constants.COLOR_MAP["purple"] rename_index = None def __init__(self, *args, **kwargs): @@ -292,13 +296,13 @@ def __init__(self, *args, **kwargs): "flame"]["create"].get(self.__class__.__name__, {}) # adding basic current context flame objects - self.project = lib.get_current_project() - self.sequence = lib.get_current_sequence(opflame.selection) + self.project = flib.get_current_project() + self.sequence = flib.get_current_sequence(opfapi_selection) if (self.options or {}).get("useSelection"): - self.selected = lib.get_sequence_segments(self.sequence, True) + self.selected = flib.get_sequence_segments(self.sequence, True) else: - self.selected = lib.get_sequence_segments(self.sequence) + self.selected = flib.get_sequence_segments(self.sequence) self.widget = CreatorWidget @@ -345,10 +349,10 @@ def __init__(self, cls, segment, **kwargs): # get main parent objects self.current_segment = segment - sequence_name = lib.get_current_sequence([segment]).name.get_value() + sequence_name = flib.get_current_sequence([segment]).name.get_value() self.sequence_name = str(sequence_name).replace(" ", "_") - self.clip_data = lib.get_segment_attributes(segment) + self.clip_data = flib.get_segment_attributes(segment) # segment (clip) main attributes self.cs_name = self.clip_data["segment_name"] self.cs_index = int(self.clip_data["segment"]) @@ -406,7 +410,7 @@ def convert(self): self.marker_data.update({"reviewTrack": None}) # create pype tag on track_item and add data - pipeline.imprint(self.current_segment, self.marker_data) + fpipeline.imprint(self.current_segment, self.marker_data) return self.current_segment diff --git a/openpype/hosts/flame/api/scripts/wiretap_com.py b/openpype/hosts/flame/api/scripts/wiretap_com.py index f1b5ab22363..0cda25804b2 100644 --- a/openpype/hosts/flame/api/scripts/wiretap_com.py +++ b/openpype/hosts/flame/api/scripts/wiretap_com.py @@ -16,7 +16,7 @@ raise KeyError("Missing key in environment `OPENPYPE_FLAME_VERSION`") try: - from libwiretapPythonClientAPI import ( + from libwiretapPythonClientAPI import ( # noqa WireTapClientInit) except ImportError: flame_python_path = "/opt/Autodesk/flame_{}/python".format(FLAME_V) @@ -26,7 +26,7 @@ sys.path.append(flame_python_path) - from libwiretapPythonClientAPI import ( + from libwiretapPythonClientAPI import ( # noqa WireTapClientInit, WireTapClientUninit, WireTapNodeHandle, diff --git a/openpype/hosts/flame/api/utility_scripts/openpype_in_flame.py b/openpype/hosts/flame/api/utility_scripts/openpype_in_flame.py index c5fa881f3c8..6e7cebd997f 100644 --- a/openpype/hosts/flame/api/utility_scripts/openpype_in_flame.py +++ b/openpype/hosts/flame/api/utility_scripts/openpype_in_flame.py @@ -5,17 +5,14 @@ import atexit import openpype import avalon -import openpype.hosts.flame as opflame - -flh = sys.modules[__name__] -flh._project = None +import openpype.hosts.flame.api as opfapi def openpype_install(): """Registering OpenPype in context """ openpype.install() - avalon.api.install(opflame) + avalon.api.install(opfapi) print("Avalon registred hosts: {}".format( avalon.api.registered_host())) @@ -48,19 +45,19 @@ def exeption_handler(exctype, value, _traceback): def cleanup(): """Cleaning up Flame framework context """ - if opflame.apps: + if opfapi.CTX.apps: print('`{}` cleaning up apps:\n {}\n'.format( - __file__, pformat(opflame.apps))) - while len(opflame.apps): - app = opflame.apps.pop() + __file__, pformat(opfapi.CTX.apps))) + while len(opfapi.CTX.apps): + app = opfapi.CTX.apps.pop() print('`{}` removing : {}'.format(__file__, app.name)) del app - opflame.apps = [] + opfapi.CTX.apps = [] - if opflame.app_framework: - print('PYTHON\t: %s cleaning up' % opflame.app_framework.bundle_name) - opflame.app_framework.save_prefs() - opflame.app_framework = None + if opfapi.CTX.app_framework: + print('PYTHON\t: %s cleaning up' % opfapi.CTX.app_framework.bundle_name) + opfapi.CTX.app_framework.save_prefs() + opfapi.CTX.app_framework = None atexit.register(cleanup) @@ -69,9 +66,9 @@ def cleanup(): def load_apps(): """Load available apps into Flame framework """ - opflame.apps.append(opflame.FlameMenuProjectConnect(opflame.app_framework)) - opflame.apps.append(opflame.FlameMenuTimeline(opflame.app_framework)) - opflame.app_framework.log.info("Apps are loaded") + opfapi.CTX.apps.append(opfapi.FlameMenuProjectConnect(opfapi.CTX.app_framework)) + opfapi.CTX.apps.append(opfapi.FlameMenuTimeline(opfapi.CTX.app_framework)) + opfapi.CTX.app_framework.log.info("Apps are loaded") def project_changed_dict(info): @@ -89,10 +86,10 @@ def app_initialized(parent=None): Args: parent (obj, optional): Parent object. Defaults to None. """ - opflame.app_framework = opflame.FlameAppFramework() + opfapi.CTX.app_framework = opfapi.FlameAppFramework() print("{} initializing".format( - opflame.app_framework.bundle_name)) + opfapi.CTX.app_framework.bundle_name)) load_apps() @@ -131,15 +128,15 @@ def _build_app_menu(app_name): # first find the relative appname app = None - for _app in opflame.apps: + for _app in opfapi.CTX.apps: if _app.__class__.__name__ == app_name: app = _app if app: menu.append(app.build_menu()) - if opflame.app_framework: - menu_auto_refresh = opflame.app_framework.prefs_global.get( + if opfapi.CTX.app_framework: + menu_auto_refresh = opfapi.CTX.app_framework.prefs_global.get( 'menu_auto_refresh', {}) if menu_auto_refresh.get('timeline_menu', True): try: @@ -163,8 +160,8 @@ def project_saved(project_name, save_time, is_auto_save): save_time (str): time when it was saved is_auto_save (bool): autosave is on or off """ - if opflame.app_framework: - opflame.app_framework.save_prefs() + if opfapi.CTX.app_framework: + opfapi.CTX.app_framework.save_prefs() def get_main_menu_custom_ui_actions(): diff --git a/openpype/hosts/flame/api/utils.py b/openpype/hosts/flame/api/utils.py index 8ed8613b15f..b9899900f5d 100644 --- a/openpype/hosts/flame/api/utils.py +++ b/openpype/hosts/flame/api/utils.py @@ -5,7 +5,7 @@ import os import shutil from openpype.api import Logger -log = Logger().get_logger(__name__) +log = Logger.get_logger(__name__) def _sync_utility_scripts(env=None): diff --git a/openpype/hosts/flame/api/workio.py b/openpype/hosts/flame/api/workio.py index d2e24087981..0c96c0752ab 100644 --- a/openpype/hosts/flame/api/workio.py +++ b/openpype/hosts/flame/api/workio.py @@ -8,7 +8,7 @@ # ) -log = Logger().get_logger(__name__) +log = Logger.get_logger(__name__) exported_projet_ext = ".otoc" diff --git a/openpype/hosts/flame/hooks/pre_flame_setup.py b/openpype/hosts/flame/hooks/pre_flame_setup.py index e7ef8569070..5e0ead94146 100644 --- a/openpype/hosts/flame/hooks/pre_flame_setup.py +++ b/openpype/hosts/flame/hooks/pre_flame_setup.py @@ -6,6 +6,7 @@ from openpype.lib import ( PreLaunchHook, get_openpype_username) from openpype.hosts import flame as opflame +import openpype.hosts.flame.api as opfapi import openpype from pprint import pformat @@ -79,7 +80,7 @@ def execute(self): app_arguments = self._get_launch_arguments(data_to_script) - opflame.setup(self.launch_context.env) + opfapi.setup(self.launch_context.env) self.launch_context.launch_args.extend(app_arguments) diff --git a/openpype/hosts/flame/otio/flame_export.py b/openpype/hosts/flame/otio/flame_export.py index aea1f387e84..bea30b58bd8 100644 --- a/openpype/hosts/flame/otio/flame_export.py +++ b/openpype/hosts/flame/otio/flame_export.py @@ -11,7 +11,7 @@ import flame from pprint import pformat -reload(utils) # noqa +reload(utils) # type: ignore log = logging.getLogger(__name__) diff --git a/openpype/hosts/flame/plugins/create/create_shot_clip.py b/openpype/hosts/flame/plugins/create/create_shot_clip.py index 45c4557dad6..70b2908bec0 100644 --- a/openpype/hosts/flame/plugins/create/create_shot_clip.py +++ b/openpype/hosts/flame/plugins/create/create_shot_clip.py @@ -1,9 +1,8 @@ from copy import deepcopy -import openpype.hosts.flame as opflame -import openpype.hosts.flame.api.plugin as fplugin -import openpype.hosts.flame.api.lib as flib -reload(fplugin) -reload(flib) +import openpype.hosts.flame.api as opfapi + +reload(opfapi) # noqa + def _get_video_track_names(sequence): track_names = [] @@ -13,7 +12,8 @@ def _get_video_track_names(sequence): return track_names -class CreateShotClip(fplugin.Creator): + +class CreateShotClip(opfapi.Creator): """Publishable clip""" label = "Create Publishable Clip" @@ -22,7 +22,7 @@ class CreateShotClip(fplugin.Creator): defaults = ["Main"] gui_tracks = _get_video_track_names( - flib.get_current_sequence(opflame.selection) + opfapi.get_current_sequence(opfapi.CTX.selection) ) gui_name = "Pype publish attributes creator" gui_info = "Define sequential rename and fill hierarchy data." @@ -267,4 +267,4 @@ def process(self): self.rename_index = i # convert track item to timeline media pool item - fplugin.PublishableClip(self, segment, **kwargs).convert() + opfapi.PublishableClip(self, segment, **kwargs).convert() diff --git a/openpype/hosts/flame/plugins/publish/collect_test_selection.py b/openpype/hosts/flame/plugins/publish/collect_test_selection.py index 29ca08d9b51..97de4e8dde1 100644 --- a/openpype/hosts/flame/plugins/publish/collect_test_selection.py +++ b/openpype/hosts/flame/plugins/publish/collect_test_selection.py @@ -1,12 +1,10 @@ import os import pyblish.api -import openpype.hosts.flame as opflame +import openpype.hosts.flame.api as opfapi from openpype.hosts.flame.otio import flame_export as otio_export -from openpype.hosts.flame.api import lib, pipeline from pprint import pformat -reload(lib) # noqa -reload(pipeline) # noqa -reload(otio_export) # noqa +reload(opfapi) # type: ignore +reload(otio_export) # type: ignore @pyblish.api.log @@ -20,9 +18,9 @@ class CollectTestSelection(pyblish.api.ContextPlugin): def process(self, context): self.log.info( - "Active Selection: {}".format(opflame.selection)) + "Active Selection: {}".format(opfapi.CTX.selection)) - sequence = lib.get_current_sequence(opflame.selection) + sequence = opfapi.get_current_sequence(opfapi.CTX.selection) self.test_imprint_data(sequence) self.test_otio_export(sequence) @@ -43,15 +41,15 @@ def test_otio_export(self, sequence): self.log.info("Otio exported to: {}".format(export_path)) def test_imprint_data(self, sequence): - with lib.maintained_segment_selection(sequence) as selected_segments: - for segment in selected_segments: + with opfapi.maintained_segment_selection(sequence) as sel_segments: + for segment in sel_segments: if str(segment.name)[1:-1] == "": continue self.log.debug("Segment with OpenPypeData: {}".format( segment.name)) - pipeline.imprint(segment, { + opfapi.imprint(segment, { 'asset': segment.name.get_value(), 'family': 'render', 'subset': 'subsetMain' From b12b27e505619a115d641eb09dba365539f5b7a9 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 7 Jan 2022 14:40:00 +0100 Subject: [PATCH 410/492] OP-2204 - added customization of bot appearance --- openpype/modules/slack/manifest.yml | 1 + .../slack/plugins/publish/integrate_slack_api.py | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/openpype/modules/slack/manifest.yml b/openpype/modules/slack/manifest.yml index 37d4669903c..bd920ac2669 100644 --- a/openpype/modules/slack/manifest.yml +++ b/openpype/modules/slack/manifest.yml @@ -15,6 +15,7 @@ oauth_config: scopes: bot: - chat:write + - chat:write.customize - chat:write.public - files:write settings: diff --git a/openpype/modules/slack/plugins/publish/integrate_slack_api.py b/openpype/modules/slack/plugins/publish/integrate_slack_api.py index b9f4b9d81fa..dd2d4ca0486 100644 --- a/openpype/modules/slack/plugins/publish/integrate_slack_api.py +++ b/openpype/modules/slack/plugins/publish/integrate_slack_api.py @@ -24,6 +24,10 @@ class IntegrateSlackAPI(pyblish.api.InstancePlugin): optional = True + # internal, not configurable + bot_user_name = "OpenpypeNotifier" + icon_url = "https://openpype.io/img/favicon/favicon.ico" + def process(self, instance): thumbnail_path = self._get_thumbnail_path(instance) review_path = self._get_review_path(instance) @@ -148,7 +152,9 @@ def _python2_call(self, token, channel, message, response = client.api_call( "chat.postMessage", channel=channel, - text=message + text=message, + username=self.bot_user_name, + icon_url=self.icon_url ) if response.get("error"): @@ -180,7 +186,9 @@ def _python3_call(self, token, channel, message, _ = client.chat_postMessage( channel=channel, - text=message + text=message, + username=self.bot_user_name, + icon_url=self.icon_url ) except SlackApiError as e: # You will get a SlackApiError if "ok" is False From 8121a532db97e45f8414a457678d7e250f703acc Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 7 Jan 2022 14:40:32 +0100 Subject: [PATCH 411/492] hound_ suggestions --- openpype/hosts/flame/api/menu.py | 2 +- openpype/hosts/flame/api/pipeline.py | 3 ++- .../hosts/flame/api/utility_scripts/openpype_in_flame.py | 7 +++++-- openpype/hosts/flame/hooks/pre_flame_setup.py | 1 - openpype/hosts/flame/otio/flame_export.py | 2 +- .../hosts/flame/plugins/publish/collect_test_selection.py | 6 +++--- 6 files changed, 12 insertions(+), 9 deletions(-) diff --git a/openpype/hosts/flame/api/menu.py b/openpype/hosts/flame/api/menu.py index 642c40a7df4..c4a18496d30 100644 --- a/openpype/hosts/flame/api/menu.py +++ b/openpype/hosts/flame/api/menu.py @@ -29,7 +29,7 @@ def callback_selection(selection, function): import openpype.hosts.flame.api as opfapi opfapi.CTX.selection = selection print("Hook Selection: \n\t{}".format( - pformat({type(item): item.name for item in CTX.selection}) + pformat({type(item): item.name for item in opfapi.CTX.selection}) )) function() diff --git a/openpype/hosts/flame/api/pipeline.py b/openpype/hosts/flame/api/pipeline.py index 5333a072101..f454c33209f 100644 --- a/openpype/hosts/flame/api/pipeline.py +++ b/openpype/hosts/flame/api/pipeline.py @@ -163,6 +163,7 @@ def imprint(segment, data=None): # add publish attribute set_publish_attribute(segment, True) + @contextlib.contextmanager def maintained_selection(): import flame @@ -175,4 +176,4 @@ def maintained_selection(): with maintained_segment_selection(sequence): yield finally: - pass \ No newline at end of file + pass diff --git a/openpype/hosts/flame/api/utility_scripts/openpype_in_flame.py b/openpype/hosts/flame/api/utility_scripts/openpype_in_flame.py index 6e7cebd997f..c385fbb8cbf 100644 --- a/openpype/hosts/flame/api/utility_scripts/openpype_in_flame.py +++ b/openpype/hosts/flame/api/utility_scripts/openpype_in_flame.py @@ -55,7 +55,9 @@ def cleanup(): opfapi.CTX.apps = [] if opfapi.CTX.app_framework: - print('PYTHON\t: %s cleaning up' % opfapi.CTX.app_framework.bundle_name) + print('openpype\t: {} cleaning up'.format( + opfapi.CTX.app_framework.bundle_name) + ) opfapi.CTX.app_framework.save_prefs() opfapi.CTX.app_framework = None @@ -66,7 +68,8 @@ def cleanup(): def load_apps(): """Load available apps into Flame framework """ - opfapi.CTX.apps.append(opfapi.FlameMenuProjectConnect(opfapi.CTX.app_framework)) + opfapi.CTX.apps.append( + opfapi.FlameMenuProjectConnect(opfapi.CTX.app_framework)) opfapi.CTX.apps.append(opfapi.FlameMenuTimeline(opfapi.CTX.app_framework)) opfapi.CTX.app_framework.log.info("Apps are loaded") diff --git a/openpype/hosts/flame/hooks/pre_flame_setup.py b/openpype/hosts/flame/hooks/pre_flame_setup.py index 5e0ead94146..512433b7182 100644 --- a/openpype/hosts/flame/hooks/pre_flame_setup.py +++ b/openpype/hosts/flame/hooks/pre_flame_setup.py @@ -79,7 +79,6 @@ def execute(self): app_arguments = self._get_launch_arguments(data_to_script) - opfapi.setup(self.launch_context.env) self.launch_context.launch_args.extend(app_arguments) diff --git a/openpype/hosts/flame/otio/flame_export.py b/openpype/hosts/flame/otio/flame_export.py index bea30b58bd8..aea1f387e84 100644 --- a/openpype/hosts/flame/otio/flame_export.py +++ b/openpype/hosts/flame/otio/flame_export.py @@ -11,7 +11,7 @@ import flame from pprint import pformat -reload(utils) # type: ignore +reload(utils) # noqa log = logging.getLogger(__name__) diff --git a/openpype/hosts/flame/plugins/publish/collect_test_selection.py b/openpype/hosts/flame/plugins/publish/collect_test_selection.py index 97de4e8dde1..0431bd1fe34 100644 --- a/openpype/hosts/flame/plugins/publish/collect_test_selection.py +++ b/openpype/hosts/flame/plugins/publish/collect_test_selection.py @@ -3,8 +3,8 @@ import openpype.hosts.flame.api as opfapi from openpype.hosts.flame.otio import flame_export as otio_export from pprint import pformat -reload(opfapi) # type: ignore -reload(otio_export) # type: ignore +reload(opfapi) # noqa +reload(otio_export) # noqa @pyblish.api.log @@ -35,7 +35,7 @@ def test_otio_export(self, sequence): otio_timeline = otio_export.create_otio_timeline(sequence) otio_export.write_to_file( otio_timeline, export_path - ) + ) self.log.info(pformat(otio_timeline)) self.log.info("Otio exported to: {}".format(export_path)) From 6ca2ae704bbc76e61dbc439cff23dee8cb25f85d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 7 Jan 2022 14:50:17 +0100 Subject: [PATCH 412/492] Update openpype/modules/slack/plugins/publish/integrate_slack_api.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- .../plugins/publish/integrate_slack_api.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/openpype/modules/slack/plugins/publish/integrate_slack_api.py b/openpype/modules/slack/plugins/publish/integrate_slack_api.py index b9f4b9d81fa..e094c268da9 100644 --- a/openpype/modules/slack/plugins/publish/integrate_slack_api.py +++ b/openpype/modules/slack/plugins/publish/integrate_slack_api.py @@ -75,16 +75,13 @@ def _get_filled_message(self, message_templ, instance, review_path=None): if review_path: fill_pairs.append(("review_link", review_path)) - task_on_instance = instance.data.get("task") - task_on_anatomy = fill_data.get("task") - if task_on_instance: - fill_pairs.append(("task[name]", task_on_instance.get("type"))) - fill_pairs.append(("task[name]", task_on_instance.get("name"))) - fill_pairs.append(("task[short]", task_on_instance.get("short"))) - elif task_on_anatomy: - fill_pairs.append(("task[name]", task_on_anatomy.get("type"))) - fill_pairs.append(("task[name]", task_on_anatomy.get("name"))) - fill_pairs.append(("task[short]", task_on_anatomy.get("short"))) + task_data = instance.data.get("task") + if not task_data: + task_data = fill_data.get("task") + for key, value in task_data.items(): + fill_key = "task[{}]".format(key) + fill_pairs.append((fill_key , value)) + fill_pairs.append(("task", task_data["name"])) self.log.debug("fill_pairs ::{}".format(fill_pairs)) multiple_case_variants = prepare_template_data(fill_pairs) From 69b9c06f6a79236ea1c9ceeaee83ef79db5a29fd Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 7 Jan 2022 14:57:32 +0100 Subject: [PATCH 413/492] OP-2204 - renamed placeholder to review_filepath --- .../slack/plugins/publish/integrate_slack_api.py | 4 ++-- website/docs/module_slack.md | 15 +++------------ 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/openpype/modules/slack/plugins/publish/integrate_slack_api.py b/openpype/modules/slack/plugins/publish/integrate_slack_api.py index e094c268da9..cdc90a7a28d 100644 --- a/openpype/modules/slack/plugins/publish/integrate_slack_api.py +++ b/openpype/modules/slack/plugins/publish/integrate_slack_api.py @@ -15,7 +15,7 @@ class IntegrateSlackAPI(pyblish.api.InstancePlugin): If instance contains 'thumbnail' it uploads it. Bot must be present in the target channel. If instance contains 'review' it could upload (if configured) or place - link with {review_link} placeholder. + link with {review_filepath} placeholder. Message template can contain {} placeholders from anatomyData. """ order = pyblish.api.IntegratorOrder + 0.499 @@ -73,7 +73,7 @@ def _get_filled_message(self, message_templ, instance, review_path=None): fill_data.get("version")))) ] if review_path: - fill_pairs.append(("review_link", review_path)) + fill_pairs.append(("review_filepath", review_path)) task_data = instance.data.get("task") if not task_data: diff --git a/website/docs/module_slack.md b/website/docs/module_slack.md index 10d5e58eac3..d74ff3a2907 100644 --- a/website/docs/module_slack.md +++ b/website/docs/module_slack.md @@ -78,23 +78,14 @@ Message content can use Templating (see [Available template keys](admin_settings Few keys also have Capitalized and UPPERCASE format. Values will be modified accordingly ({Asset} >> "Asset", {FAMILY} >> "RENDER"). -**Available keys:** -- asset -- subset -- task\[name\] -- task\[type\] -- task\[short\] -- username -- app -- family -- version -- review_link +**Additional implemented keys:** +- review_filepath ##### Message example ``` {Subset} was published for {ASSET} in {task[name]} task. -Here you can find review {review_link} +Here you can find review {review_filepath} ``` #### Message retention From 75b828022103a7385a5c25116d1bcb54967fa833 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 7 Jan 2022 15:07:13 +0100 Subject: [PATCH 414/492] flame: review comment https://github.com/pypeclub/OpenPype/pull/2495#discussion_r779708538 --- .../hosts/flame/api/scripts/wiretap_com.py | 33 +++++-------------- openpype/hosts/flame/hooks/pre_flame_setup.py | 20 ++++++++--- .../system_settings/applications.json | 3 +- 3 files changed, 25 insertions(+), 31 deletions(-) diff --git a/openpype/hosts/flame/api/scripts/wiretap_com.py b/openpype/hosts/flame/api/scripts/wiretap_com.py index 0cda25804b2..4e54dfd9137 100644 --- a/openpype/hosts/flame/api/scripts/wiretap_com.py +++ b/openpype/hosts/flame/api/scripts/wiretap_com.py @@ -9,31 +9,14 @@ import xml.dom.minidom as minidom from copy import deepcopy import datetime - -FLAME_V = os.getenv("OPENPYPE_FLAME_VERSION") - -if not FLAME_V: - raise KeyError("Missing key in environment `OPENPYPE_FLAME_VERSION`") - -try: - from libwiretapPythonClientAPI import ( # noqa - WireTapClientInit) -except ImportError: - flame_python_path = "/opt/Autodesk/flame_{}/python".format(FLAME_V) - flame_exe_path = ( - "/opt/Autodesk/flame_{}/bin/flame.app" - "/Contents/MacOS/startApp").format(FLAME_V) - - sys.path.append(flame_python_path) - - from libwiretapPythonClientAPI import ( # noqa - WireTapClientInit, - WireTapClientUninit, - WireTapNodeHandle, - WireTapServerHandle, - WireTapInt, - WireTapStr - ) +from libwiretapPythonClientAPI import ( # noqa + WireTapClientInit, + WireTapClientUninit, + WireTapNodeHandle, + WireTapServerHandle, + WireTapInt, + WireTapStr +) class WireTapCom(object): diff --git a/openpype/hosts/flame/hooks/pre_flame_setup.py b/openpype/hosts/flame/hooks/pre_flame_setup.py index 512433b7182..fc6b65c958d 100644 --- a/openpype/hosts/flame/hooks/pre_flame_setup.py +++ b/openpype/hosts/flame/hooks/pre_flame_setup.py @@ -20,10 +20,8 @@ class FlamePrelaunch(PreLaunchHook): app_groups = ["flame"] # todo: replace version number with avalon launch app version - flame_python_exe = ( - "/opt/Autodesk/python/{OPENPYPE_FLAME_VERSION}" - "/bin/python2.7" - ) + flame_python_exe = os.getenv("OPENPYPE_FLAME_PYTHON_EXEC") + flame_pythonpath = os.getenv("OPENPYPE_FLAME_PYTHONPATH") wtc_script_path = os.path.join( opflame.HOST_DIR, "api", "scripts", "wiretap_com.py") @@ -60,7 +58,6 @@ def execute(self): "FieldDominance": "PROGRESSIVE" } - data_to_script = { # from settings "host_name": _env.get("FLAME_WIRETAP_HOSTNAME") or hostname, @@ -77,12 +74,25 @@ def execute(self): self.log.info(pformat(dict(_env))) self.log.info(pformat(data_to_script)) + # add to python path from settings + self._add_pythonpath() + app_arguments = self._get_launch_arguments(data_to_script) opfapi.setup(self.launch_context.env) self.launch_context.launch_args.extend(app_arguments) + def _add_pythonpath(self): + pythonpath = self.launch_context.env.get("PYTHONPATH") + + # separate it explicity by `;` that is what we use in settings + new_pythonpath = self.flame_pythonpath.split(";") + new_pythonpath += pythonpath.split(os.pathsep) + + self.launch_context.env["PYTHONPATH"] = os.pathsep.join(new_pythonpath) + + def _get_launch_arguments(self, script_data): # Dump data to string dumped_script_data = json.dumps(script_data) diff --git a/openpype/settings/defaults/system_settings/applications.json b/openpype/settings/defaults/system_settings/applications.json index 3a097d2b370..7fe0432fdf4 100644 --- a/openpype/settings/defaults/system_settings/applications.json +++ b/openpype/settings/defaults/system_settings/applications.json @@ -130,7 +130,8 @@ "linux": [] }, "environment": { - "OPENPYPE_FLAME_VERSION": "2021" + "OPENPYPE_FLAME_PYTHON_EXEC": "/opt/Autodesk/python/2021/bin/python2.7", + "OPENPYPE_FLAME_PYTHONPATH": "/opt/Autodesk/flame_2021/python" } }, "__dynamic_keys_labels__": { From 8a7c4772aaf4838b1cdaa03bc454b848b9eacf23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Fri, 7 Jan 2022 15:09:45 +0100 Subject: [PATCH 415/492] Update openpype/hosts/flame/api/plugin.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/hosts/flame/api/plugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index 4f71f9424e7..b291a6ea06b 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -134,9 +134,9 @@ def camel_case_split(self, text): '.+?(?:(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|$)', text) return " ".join([str(m.group(0)).capitalize() for m in matches]) - def create_row(self, layout, type, text, **kwargs): + def create_row(self, layout, type_name, text, **kwargs): # get type attribute from qwidgets - attr = getattr(QtWidgets, type) + attr = getattr(QtWidgets, type_name) # convert label text to normal capitalized text with spaces label_text = self.camel_case_split(text) From 389e90670ed5181d9dd45211950f67f9aec1cc5e Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 7 Jan 2022 15:16:40 +0100 Subject: [PATCH 416/492] flame: improving creator gui --- openpype/hosts/flame/api/plugin.py | 29 ++++--------------- .../flame/plugins/create/create_shot_clip.py | 2 +- 2 files changed, 7 insertions(+), 24 deletions(-) diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index 4f71f9424e7..3f93262e6f3 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -38,7 +38,7 @@ def __init__(self, name, info, ui_inputs, parent=None): self.content_widget = [QtWidgets.QWidget(self)] top_layout = QtWidgets.QFormLayout(self.content_widget[0]) top_layout.setObjectName("ContentLayout") - top_layout.addWidget(Spacer(5, self)) + top_layout.addSpacing(5) # first add widget tag line top_layout.addWidget(QtWidgets.QLabel(info)) @@ -202,13 +202,13 @@ def populate_widgets(self, data, content_layout=None): v = data[k] tool_tip = v.get("toolTip", "") if v["type"] == "dict": - # adding spacer between sections self.content_layout.append(QtWidgets.QWidget(self)) content_layout.addWidget(self.content_layout[-1]) self.content_layout[-1].setObjectName("sectionHeadline") headline = QtWidgets.QVBoxLayout(self.content_layout[-1]) - headline.addWidget(Spacer(20, self)) + headline.addSpacing(20) + headline.addWidget(QtWidgets.QLabel(v["label"])) # adding nested layout with label @@ -225,13 +225,12 @@ def populate_widgets(self, data, content_layout=None): v["value"], nested_content_layout) if v["type"] == "section": - # adding spacer between sections self.content_layout.append(QtWidgets.QWidget(self)) content_layout.addWidget(self.content_layout[-1]) self.content_layout[-1].setObjectName("sectionHeadline") headline = QtWidgets.QVBoxLayout(self.content_layout[-1]) - headline.addWidget(Spacer(20, self)) + headline.addSpacing(20) headline.addWidget(QtWidgets.QLabel(v["label"])) # adding nested layout with label @@ -267,23 +266,6 @@ def populate_widgets(self, data, content_layout=None): return data -class Spacer(QtWidgets.QWidget): - def __init__(self, height, *args, **kwargs): - super(self.__class__, self).__init__(*args, **kwargs) - - self.setFixedHeight(height) - - real_spacer = QtWidgets.QWidget(self) - real_spacer.setObjectName("Spacer") - real_spacer.setFixedHeight(height) - - layout = QtWidgets.QVBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.addWidget(real_spacer) - - self.setLayout(layout) - - class Creator(openpype.Creator): """Creator class wrapper """ @@ -304,7 +286,8 @@ def __init__(self, *args, **kwargs): else: self.selected = flib.get_sequence_segments(self.sequence) - self.widget = CreatorWidget + def create_widget(self, *args, **kwargs): + return CreatorWidget(*args, **kwargs) class PublishableClip: diff --git a/openpype/hosts/flame/plugins/create/create_shot_clip.py b/openpype/hosts/flame/plugins/create/create_shot_clip.py index 70b2908bec0..7129b965ac7 100644 --- a/openpype/hosts/flame/plugins/create/create_shot_clip.py +++ b/openpype/hosts/flame/plugins/create/create_shot_clip.py @@ -232,7 +232,7 @@ def process(self): gui_inputs[k]["value"] = presets[k] # open widget for plugins inputs - widget = self.widget(self.gui_name, self.gui_info, gui_inputs) + widget = self.create_widget(self.gui_name, self.gui_info, gui_inputs) widget.exec_() if len(self.selected) < 1: From ae02ad0d86fc7cf1207017729db24e7b48f326ad Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 7 Jan 2022 15:47:35 +0100 Subject: [PATCH 417/492] flame: refactory creator plugin with abstract class --- openpype/hosts/flame/api/plugin.py | 6 +- .../flame/plugins/create/create_shot_clip.py | 413 +++++++++--------- 2 files changed, 208 insertions(+), 211 deletions(-) diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index b0f7568e50b..6122b7bf1f1 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -326,10 +326,8 @@ class PublishableClip: vertical_sync_default = False driving_layer_default = "" - def __init__(self, cls, segment, **kwargs): - # populate input cls attribute onto self.[attr] - self.__dict__.update(cls.__dict__) - + def __init__(self, segment, **kwargs): + self.rename_index = kwargs["rename_index"] # get main parent objects self.current_segment = segment sequence_name = flib.get_current_sequence([segment]).name.get_value() diff --git a/openpype/hosts/flame/plugins/create/create_shot_clip.py b/openpype/hosts/flame/plugins/create/create_shot_clip.py index 7129b965ac7..866b5108fa9 100644 --- a/openpype/hosts/flame/plugins/create/create_shot_clip.py +++ b/openpype/hosts/flame/plugins/create/create_shot_clip.py @@ -4,15 +4,6 @@ reload(opfapi) # noqa -def _get_video_track_names(sequence): - track_names = [] - for ver in sequence.versions: - for track in ver.tracks: - track_names.append(track.name.get_value()) - - return track_names - - class CreateShotClip(opfapi.Creator): """Publishable clip""" @@ -21,203 +12,12 @@ class CreateShotClip(opfapi.Creator): icon = "film" defaults = ["Main"] - gui_tracks = _get_video_track_names( - opfapi.get_current_sequence(opfapi.CTX.selection) - ) - gui_name = "Pype publish attributes creator" - gui_info = "Define sequential rename and fill hierarchy data." - gui_inputs = { - "renameHierarchy": { - "type": "section", - "label": "Shot Hierarchy And Rename Settings", - "target": "ui", - "order": 0, - "value": { - "hierarchy": { - "value": "{folder}/{sequence}", - "type": "QLineEdit", - "label": "Shot Parent Hierarchy", - "target": "tag", - "toolTip": "Parents folder for shot root folder, Template filled with `Hierarchy Data` section", # noqa - "order": 0}, - "clipRename": { - "value": False, - "type": "QCheckBox", - "label": "Rename clips", - "target": "ui", - "toolTip": "Renaming selected clips on fly", # noqa - "order": 1}, - "clipName": { - "value": "{sequence}{shot}", - "type": "QLineEdit", - "label": "Clip Name Template", - "target": "ui", - "toolTip": "template for creating shot namespaused for renaming (use rename: on)", # noqa - "order": 2}, - "countFrom": { - "value": 10, - "type": "QSpinBox", - "label": "Count sequence from", - "target": "ui", - "toolTip": "Set when the sequence number stafrom", # noqa - "order": 3}, - "countSteps": { - "value": 10, - "type": "QSpinBox", - "label": "Stepping number", - "target": "ui", - "toolTip": "What number is adding every new step", # noqa - "order": 4}, - } - }, - "hierarchyData": { - "type": "dict", - "label": "Shot Template Keywords", - "target": "tag", - "order": 1, - "value": { - "folder": { - "value": "shots", - "type": "QLineEdit", - "label": "{folder}", - "target": "tag", - "toolTip": "Name of folder used for root of generated shots.\nUsable tokens:\n\t{_clip_}: name of used clip\n\t{_track_}: name of parent track layer\n\t{_sequence_}: name of parent sequence (timeline)", # noqa - "order": 0}, - "episode": { - "value": "ep01", - "type": "QLineEdit", - "label": "{episode}", - "target": "tag", - "toolTip": "Name of episode.\nUsable tokens:\n\t{_clip_}: name of used clip\n\t{_track_}: name of parent track layer\n\t{_sequence_}: name of parent sequence (timeline)", # noqa - "order": 1}, - "sequence": { - "value": "sq01", - "type": "QLineEdit", - "label": "{sequence}", - "target": "tag", - "toolTip": "Name of sequence of shots.\nUsable tokens:\n\t{_clip_}: name of used clip\n\t{_track_}: name of parent track layer\n\t{_sequence_}: name of parent sequence (timeline)", # noqa - "order": 2}, - "track": { - "value": "{_track_}", - "type": "QLineEdit", - "label": "{track}", - "target": "tag", - "toolTip": "Name of sequence of shots.\nUsable tokens:\n\t{_clip_}: name of used clip\n\t{_track_}: name of parent track layer\n\t{_sequence_}: name of parent sequence (timeline)", # noqa - "order": 3}, - "shot": { - "value": "sh###", - "type": "QLineEdit", - "label": "{shot}", - "target": "tag", - "toolTip": "Name of shot. `#` is converted to paded number. \nAlso could be used with usable tokens:\n\t{_clip_}: name of used clip\n\t{_track_}: name of parent track layer\n\t{_sequence_}: name of parent sequence (timeline)", # noqa - "order": 4} - } - }, - "verticalSync": { - "type": "section", - "label": "Vertical Synchronization Of Attributes", - "target": "ui", - "order": 2, - "value": { - "vSyncOn": { - "value": True, - "type": "QCheckBox", - "label": "Enable Vertical Sync", - "target": "ui", - "toolTip": "Switch on if you want clips above each other to share its attributes", # noqa - "order": 0}, - "vSyncTrack": { - "value": gui_tracks, # noqa - "type": "QComboBox", - "label": "Hero track", - "target": "ui", - "toolTip": "Select driving track name which should be hero for all others", # noqa - "order": 1} - } - }, - "publishSettings": { - "type": "section", - "label": "Publish Settings", - "target": "ui", - "order": 3, - "value": { - "subsetName": { - "value": ["[ track name ]", "main", "bg", "fg", "bg", - "animatic"], - "type": "QComboBox", - "label": "Subset Name", - "target": "ui", - "toolTip": "chose subset name patern, if [ track name ] is selected, name of track layer will be used", # noqa - "order": 0}, - "subsetFamily": { - "value": ["plate", "take"], - "type": "QComboBox", - "label": "Subset Family", - "target": "ui", "toolTip": "What use of this subset is for", # noqa - "order": 1}, - "reviewTrack": { - "value": ["< none >"] + gui_tracks, - "type": "QComboBox", - "label": "Use Review Track", - "target": "ui", - "toolTip": "Generate preview videos on fly, if `< none >` is defined nothing will be generated.", # noqa - "order": 2}, - "audio": { - "value": False, - "type": "QCheckBox", - "label": "Include audio", - "target": "tag", - "toolTip": "Process subsets with corresponding audio", # noqa - "order": 3}, - "sourceResolution": { - "value": False, - "type": "QCheckBox", - "label": "Source resolution", - "target": "tag", - "toolTip": "Is resloution taken from timeline or source?", # noqa - "order": 4}, - } - }, - "frameRangeAttr": { - "type": "section", - "label": "Shot Attributes", - "target": "ui", - "order": 4, - "value": { - "workfileFrameStart": { - "value": 1001, - "type": "QSpinBox", - "label": "Workfiles Start Frame", - "target": "tag", - "toolTip": "Set workfile starting frame number", # noqa - "order": 0 - }, - "handleStart": { - "value": 0, - "type": "QSpinBox", - "label": "Handle Start", - "target": "tag", - "toolTip": "Handle at start of clip", # noqa - "order": 1 - }, - "handleEnd": { - "value": 0, - "type": "QSpinBox", - "label": "Handle End", - "target": "tag", - "toolTip": "Handle at end of clip", # noqa - "order": 2 - } - } - } - } - presets = None def process(self): # Creator copy of object attributes that are modified during `process` presets = deepcopy(self.presets) - gui_inputs = deepcopy(self.gui_inputs) + gui_inputs = self.get_gui_inputs() # get key pares from presets and match it on ui inputs for k, v in gui_inputs.items(): @@ -232,7 +32,11 @@ def process(self): gui_inputs[k]["value"] = presets[k] # open widget for plugins inputs - widget = self.create_widget(self.gui_name, self.gui_info, gui_inputs) + widget = self.create_widget( + "Pype publish attributes creator", + "Define sequential rename and fill hierarchy data.", + gui_inputs + ) widget.exec_() if len(self.selected) < 1: @@ -242,8 +46,6 @@ def process(self): print("Operation aborted") return - self.rename_add = 0 - # get ui output for track name for vertical sync v_sync_track = widget.result["vSyncTrack"]["value"] @@ -264,7 +66,204 @@ def process(self): } for i, segment in enumerate(sorted_selected_segments): - self.rename_index = i - + kwargs["rename_index"] = i # convert track item to timeline media pool item - opfapi.PublishableClip(self, segment, **kwargs).convert() + opfapi.PublishableClip(segment, **kwargs).convert() + + def get_gui_inputs(self): + gui_tracks = self._get_video_track_names( + opfapi.get_current_sequence(opfapi.CTX.selection) + ) + return deepcopy({ + "renameHierarchy": { + "type": "section", + "label": "Shot Hierarchy And Rename Settings", + "target": "ui", + "order": 0, + "value": { + "hierarchy": { + "value": "{folder}/{sequence}", + "type": "QLineEdit", + "label": "Shot Parent Hierarchy", + "target": "tag", + "toolTip": "Parents folder for shot root folder, Template filled with `Hierarchy Data` section", # noqa + "order": 0}, + "clipRename": { + "value": False, + "type": "QCheckBox", + "label": "Rename clips", + "target": "ui", + "toolTip": "Renaming selected clips on fly", # noqa + "order": 1}, + "clipName": { + "value": "{sequence}{shot}", + "type": "QLineEdit", + "label": "Clip Name Template", + "target": "ui", + "toolTip": "template for creating shot namespaused for renaming (use rename: on)", # noqa + "order": 2}, + "countFrom": { + "value": 10, + "type": "QSpinBox", + "label": "Count sequence from", + "target": "ui", + "toolTip": "Set when the sequence number stafrom", # noqa + "order": 3}, + "countSteps": { + "value": 10, + "type": "QSpinBox", + "label": "Stepping number", + "target": "ui", + "toolTip": "What number is adding every new step", # noqa + "order": 4}, + } + }, + "hierarchyData": { + "type": "dict", + "label": "Shot Template Keywords", + "target": "tag", + "order": 1, + "value": { + "folder": { + "value": "shots", + "type": "QLineEdit", + "label": "{folder}", + "target": "tag", + "toolTip": "Name of folder used for root of generated shots.\nUsable tokens:\n\t{_clip_}: name of used clip\n\t{_track_}: name of parent track layer\n\t{_sequence_}: name of parent sequence (timeline)", # noqa + "order": 0}, + "episode": { + "value": "ep01", + "type": "QLineEdit", + "label": "{episode}", + "target": "tag", + "toolTip": "Name of episode.\nUsable tokens:\n\t{_clip_}: name of used clip\n\t{_track_}: name of parent track layer\n\t{_sequence_}: name of parent sequence (timeline)", # noqa + "order": 1}, + "sequence": { + "value": "sq01", + "type": "QLineEdit", + "label": "{sequence}", + "target": "tag", + "toolTip": "Name of sequence of shots.\nUsable tokens:\n\t{_clip_}: name of used clip\n\t{_track_}: name of parent track layer\n\t{_sequence_}: name of parent sequence (timeline)", # noqa + "order": 2}, + "track": { + "value": "{_track_}", + "type": "QLineEdit", + "label": "{track}", + "target": "tag", + "toolTip": "Name of sequence of shots.\nUsable tokens:\n\t{_clip_}: name of used clip\n\t{_track_}: name of parent track layer\n\t{_sequence_}: name of parent sequence (timeline)", # noqa + "order": 3}, + "shot": { + "value": "sh###", + "type": "QLineEdit", + "label": "{shot}", + "target": "tag", + "toolTip": "Name of shot. `#` is converted to paded number. \nAlso could be used with usable tokens:\n\t{_clip_}: name of used clip\n\t{_track_}: name of parent track layer\n\t{_sequence_}: name of parent sequence (timeline)", # noqa + "order": 4} + } + }, + "verticalSync": { + "type": "section", + "label": "Vertical Synchronization Of Attributes", + "target": "ui", + "order": 2, + "value": { + "vSyncOn": { + "value": True, + "type": "QCheckBox", + "label": "Enable Vertical Sync", + "target": "ui", + "toolTip": "Switch on if you want clips above each other to share its attributes", # noqa + "order": 0}, + "vSyncTrack": { + "value": gui_tracks, # noqa + "type": "QComboBox", + "label": "Hero track", + "target": "ui", + "toolTip": "Select driving track name which should be hero for all others", # noqa + "order": 1} + } + }, + "publishSettings": { + "type": "section", + "label": "Publish Settings", + "target": "ui", + "order": 3, + "value": { + "subsetName": { + "value": ["[ track name ]", "main", "bg", "fg", "bg", + "animatic"], + "type": "QComboBox", + "label": "Subset Name", + "target": "ui", + "toolTip": "chose subset name patern, if [ track name ] is selected, name of track layer will be used", # noqa + "order": 0}, + "subsetFamily": { + "value": ["plate", "take"], + "type": "QComboBox", + "label": "Subset Family", + "target": "ui", "toolTip": "What use of this subset is for", # noqa + "order": 1}, + "reviewTrack": { + "value": ["< none >"] + gui_tracks, + "type": "QComboBox", + "label": "Use Review Track", + "target": "ui", + "toolTip": "Generate preview videos on fly, if `< none >` is defined nothing will be generated.", # noqa + "order": 2}, + "audio": { + "value": False, + "type": "QCheckBox", + "label": "Include audio", + "target": "tag", + "toolTip": "Process subsets with corresponding audio", # noqa + "order": 3}, + "sourceResolution": { + "value": False, + "type": "QCheckBox", + "label": "Source resolution", + "target": "tag", + "toolTip": "Is resloution taken from timeline or source?", # noqa + "order": 4}, + } + }, + "frameRangeAttr": { + "type": "section", + "label": "Shot Attributes", + "target": "ui", + "order": 4, + "value": { + "workfileFrameStart": { + "value": 1001, + "type": "QSpinBox", + "label": "Workfiles Start Frame", + "target": "tag", + "toolTip": "Set workfile starting frame number", # noqa + "order": 0 + }, + "handleStart": { + "value": 0, + "type": "QSpinBox", + "label": "Handle Start", + "target": "tag", + "toolTip": "Handle at start of clip", # noqa + "order": 1 + }, + "handleEnd": { + "value": 0, + "type": "QSpinBox", + "label": "Handle End", + "target": "tag", + "toolTip": "Handle at end of clip", # noqa + "order": 2 + } + } + } + }) + + def _get_video_track_names(self, sequence): + track_names = [] + for ver in sequence.versions: + for track in ver.tracks: + track_names.append(track.name.get_value()) + + return track_names From d1b4ac5d40cb922dc678e472bef71d61ebeb3582 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 7 Jan 2022 15:52:19 +0100 Subject: [PATCH 418/492] moved timers manager one hierarchy higher --- openpype/modules/{default_modules => }/timers_manager/__init__.py | 0 .../modules/{default_modules => }/timers_manager/exceptions.py | 0 .../modules/{default_modules => }/timers_manager/idle_threads.py | 0 .../timers_manager/launch_hooks/post_start_timer.py | 0 openpype/modules/{default_modules => }/timers_manager/rest_api.py | 0 .../{default_modules => }/timers_manager/timers_manager.py | 0 .../{default_modules => }/timers_manager/widget_user_idle.py | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename openpype/modules/{default_modules => }/timers_manager/__init__.py (100%) rename openpype/modules/{default_modules => }/timers_manager/exceptions.py (100%) rename openpype/modules/{default_modules => }/timers_manager/idle_threads.py (100%) rename openpype/modules/{default_modules => }/timers_manager/launch_hooks/post_start_timer.py (100%) rename openpype/modules/{default_modules => }/timers_manager/rest_api.py (100%) rename openpype/modules/{default_modules => }/timers_manager/timers_manager.py (100%) rename openpype/modules/{default_modules => }/timers_manager/widget_user_idle.py (100%) diff --git a/openpype/modules/default_modules/timers_manager/__init__.py b/openpype/modules/timers_manager/__init__.py similarity index 100% rename from openpype/modules/default_modules/timers_manager/__init__.py rename to openpype/modules/timers_manager/__init__.py diff --git a/openpype/modules/default_modules/timers_manager/exceptions.py b/openpype/modules/timers_manager/exceptions.py similarity index 100% rename from openpype/modules/default_modules/timers_manager/exceptions.py rename to openpype/modules/timers_manager/exceptions.py diff --git a/openpype/modules/default_modules/timers_manager/idle_threads.py b/openpype/modules/timers_manager/idle_threads.py similarity index 100% rename from openpype/modules/default_modules/timers_manager/idle_threads.py rename to openpype/modules/timers_manager/idle_threads.py diff --git a/openpype/modules/default_modules/timers_manager/launch_hooks/post_start_timer.py b/openpype/modules/timers_manager/launch_hooks/post_start_timer.py similarity index 100% rename from openpype/modules/default_modules/timers_manager/launch_hooks/post_start_timer.py rename to openpype/modules/timers_manager/launch_hooks/post_start_timer.py diff --git a/openpype/modules/default_modules/timers_manager/rest_api.py b/openpype/modules/timers_manager/rest_api.py similarity index 100% rename from openpype/modules/default_modules/timers_manager/rest_api.py rename to openpype/modules/timers_manager/rest_api.py diff --git a/openpype/modules/default_modules/timers_manager/timers_manager.py b/openpype/modules/timers_manager/timers_manager.py similarity index 100% rename from openpype/modules/default_modules/timers_manager/timers_manager.py rename to openpype/modules/timers_manager/timers_manager.py diff --git a/openpype/modules/default_modules/timers_manager/widget_user_idle.py b/openpype/modules/timers_manager/widget_user_idle.py similarity index 100% rename from openpype/modules/default_modules/timers_manager/widget_user_idle.py rename to openpype/modules/timers_manager/widget_user_idle.py From 00118b249a6555d1c75e077399d9a3a4a87bf1f6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 7 Jan 2022 15:52:32 +0100 Subject: [PATCH 419/492] added timers manager to default modules --- openpype/modules/base.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/modules/base.py b/openpype/modules/base.py index b5c491a1c00..d566692439f 100644 --- a/openpype/modules/base.py +++ b/openpype/modules/base.py @@ -42,6 +42,7 @@ "settings_action", "standalonepublish_action", "job_queue", + "timers_manager", ) From d0faab89f96b35b297d3117bea619d2dc8cc4c5a Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 7 Jan 2022 15:55:53 +0100 Subject: [PATCH 420/492] flame: adding wiretap tools dir to app env var --- openpype/hosts/flame/api/scripts/wiretap_com.py | 17 +++++++---------- .../defaults/system_settings/applications.json | 3 ++- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/openpype/hosts/flame/api/scripts/wiretap_com.py b/openpype/hosts/flame/api/scripts/wiretap_com.py index 4e54dfd9137..2cd9a461844 100644 --- a/openpype/hosts/flame/api/scripts/wiretap_com.py +++ b/openpype/hosts/flame/api/scripts/wiretap_com.py @@ -43,6 +43,9 @@ def __init__(self, host_name=None, volume_name=None, group_name=None): self.volume_name = volume_name or "stonefs" self.group_name = group_name or "staff" + # wiretap tools dir path + self.wiretap_tools_dir = os.getenv("OPENPYPE_WIRETAP_TOOLS") + # initialize WireTap client WireTapClientInit() @@ -166,11 +169,8 @@ def _project_prep(self, project_name): # form cmd arguments project_create_cmd = [ os.path.join( - "/opt/Autodesk/", - "wiretap", - "tools", - FLAME_V, - "wiretap_create_node", + self.wiretap_tools_dir, + "wiretap_create_node" ), '-n', os.path.join("/volumes", self.volume_name), @@ -422,11 +422,8 @@ def _set_project_colorspace(self, project_name, color_policy): color_policy = color_policy or "Legacy" project_colorspace_cmd = [ os.path.join( - "/opt/Autodesk/", - "wiretap", - "tools", - FLAME_V, - "wiretap_duplicate_node", + self.wiretap_tools_dir, + "wiretap_duplicate_node" ), "-s", "/syncolor/policies/Autodesk/{}".format(color_policy), diff --git a/openpype/settings/defaults/system_settings/applications.json b/openpype/settings/defaults/system_settings/applications.json index 7fe0432fdf4..4a8b6d82a24 100644 --- a/openpype/settings/defaults/system_settings/applications.json +++ b/openpype/settings/defaults/system_settings/applications.json @@ -131,7 +131,8 @@ }, "environment": { "OPENPYPE_FLAME_PYTHON_EXEC": "/opt/Autodesk/python/2021/bin/python2.7", - "OPENPYPE_FLAME_PYTHONPATH": "/opt/Autodesk/flame_2021/python" + "OPENPYPE_FLAME_PYTHONPATH": "/opt/Autodesk/flame_2021/python", + "OPENPYPE_WIRETAP_TOOLS": "/opt/Autodesk/wiretap/tools/2021" } }, "__dynamic_keys_labels__": { From a24b43451dde32333eec70d410fd8791a29fb696 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 7 Jan 2022 16:18:11 +0100 Subject: [PATCH 421/492] flame: fix wrong selection import --- openpype/hosts/flame/api/__init__.py | 2 -- openpype/hosts/flame/api/lib.py | 34 ---------------------------- openpype/hosts/flame/api/plugin.py | 3 +-- 3 files changed, 1 insertion(+), 38 deletions(-) diff --git a/openpype/hosts/flame/api/__init__.py b/openpype/hosts/flame/api/__init__.py index c8660aafc4d..dc47488dc1d 100644 --- a/openpype/hosts/flame/api/__init__.py +++ b/openpype/hosts/flame/api/__init__.py @@ -11,7 +11,6 @@ from .lib import ( CTX, FlameAppFramework, - maintain_current_timeline, get_project_manager, get_current_project, get_current_sequence, @@ -68,7 +67,6 @@ # lib "CTX", "FlameAppFramework", - "maintain_current_timeline", "get_project_manager", "get_current_project", "get_current_sequence", diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index ccc664ce63e..b37cc35afd7 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -225,40 +225,6 @@ def save_prefs(self): return True -@contextlib.contextmanager -def maintain_current_timeline(to_timeline, from_timeline=None): - """Maintain current timeline selection during context - - Attributes: - from_timeline (resolve.Timeline)[optional]: - Example: - >>> print(from_timeline.GetName()) - timeline1 - >>> print(to_timeline.GetName()) - timeline2 - - >>> with maintain_current_timeline(to_timeline): - ... print(get_current_sequence().GetName()) - timeline2 - - >>> print(get_current_sequence().GetName()) - timeline1 - """ - # todo: this is still Resolve's implementation - project = get_current_project() - working_timeline = from_timeline or project.GetCurrentTimeline() - - # swith to the input timeline - project.SetCurrentTimeline(to_timeline) - - try: - # do a work - yield - finally: - # put the original working timeline to context - project.SetCurrentTimeline(working_timeline) - - def get_project_manager(): # TODO: get_project_manager return diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index 6122b7bf1f1..1ae62f3a8d5 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -2,7 +2,6 @@ from Qt import QtWidgets, QtCore import openpype.api as openpype from openpype import style -from . import selection as opfapi_selection from . import ( lib as flib, pipeline as fpipeline, @@ -279,7 +278,7 @@ def __init__(self, *args, **kwargs): # adding basic current context flame objects self.project = flib.get_current_project() - self.sequence = flib.get_current_sequence(opfapi_selection) + self.sequence = flib.get_current_sequence(flib.CTX.selection) if (self.options or {}).get("useSelection"): self.selected = flib.get_sequence_segments(self.sequence, True) From b56fabbf8c01e2e5b82112e340c2a2a835018afd Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 7 Jan 2022 16:34:28 +0100 Subject: [PATCH 422/492] added new image and define new color for delete button --- openpype/style/data.json | 3 +++ openpype/style/style.css | 7 ++++++ .../project_manager/images/warning.png | Bin 0 -> 9393 bytes .../project_manager/project_manager/style.py | 21 ++++++++++++++++++ 4 files changed, 31 insertions(+) create mode 100644 openpype/tools/project_manager/project_manager/images/warning.png diff --git a/openpype/style/data.json b/openpype/style/data.json index 026eaf42648..205e30563bc 100644 --- a/openpype/style/data.json +++ b/openpype/style/data.json @@ -51,6 +51,9 @@ "border-hover": "rgba(168, 175, 189, .3)", "border-focus": "rgb(92, 173, 214)", + "delete-btn-bg": "rgb(201, 54, 54)", + "delete-btn-bg-disabled": "rgba(201, 54, 54, 64)", + "tab-widget": { "bg": "#21252B", "bg-selected": "#434a56", diff --git a/openpype/style/style.css b/openpype/style/style.css index 4159fe1676f..8f613f38888 100644 --- a/openpype/style/style.css +++ b/openpype/style/style.css @@ -713,6 +713,13 @@ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { background: {color:bg-view-hover}; } +#DeleteButton { + background: {color:delete-btn-bg}; +} +#DeleteButton:disabled { + background: {color:delete-btn-bg-disabled}; +} + /* Launcher specific stylesheets */ #IconView[mode="icon"] { /* font size can't be set on items */ diff --git a/openpype/tools/project_manager/project_manager/images/warning.png b/openpype/tools/project_manager/project_manager/images/warning.png new file mode 100644 index 0000000000000000000000000000000000000000..3b4ae861f9f3982195c82d681d4b3f31cdea9df8 GIT binary patch literal 9393 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 H|MdR=mHnnz literal 0 HcmV?d00001 diff --git a/openpype/tools/project_manager/project_manager/style.py b/openpype/tools/project_manager/project_manager/style.py index d3d6857a63b..9fa7a5520bc 100644 --- a/openpype/tools/project_manager/project_manager/style.py +++ b/openpype/tools/project_manager/project_manager/style.py @@ -1,6 +1,7 @@ import os from Qt import QtCore, QtGui +from openpype.style import get_objected_colors from avalon.vendor import qtawesome @@ -90,6 +91,17 @@ def get_remove_icon(cls): icon.addPixmap(disabled_pix, QtGui.QIcon.Disabled, QtGui.QIcon.Off) return icon + @classmethod + def get_warning_pixmap(cls): + src_image = get_warning_image() + colors = get_objected_colors() + color_value = colors["delete-btn-bg"] + + return paint_image_with_color( + src_image, + color_value.get_qcolor() + ) + def get_remove_image(): image_path = os.path.join( @@ -100,6 +112,15 @@ def get_remove_image(): return QtGui.QImage(image_path) +def get_warning_image(): + image_path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "images", + "warning.png" + ) + return QtGui.QImage(image_path) + + def paint_image_with_color(image, color): """TODO: This function should be imported from utils. From 53ad6027c5d30c365a8ea52da59f83b8c708a169 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 7 Jan 2022 16:36:26 +0100 Subject: [PATCH 423/492] changed buttons classes and mark delete btn with DeleteButton object name --- openpype/tools/project_manager/project_manager/widgets.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/tools/project_manager/project_manager/widgets.py b/openpype/tools/project_manager/project_manager/widgets.py index 392f3f45039..20a6955d817 100644 --- a/openpype/tools/project_manager/project_manager/widgets.py +++ b/openpype/tools/project_manager/project_manager/widgets.py @@ -352,9 +352,10 @@ def __init__(self, project_name, parent): "Type \"{}\" to confirm...".format(project_name) ) - cancel_btn = _SameSizeBtns("Cancel", self) + cancel_btn = QtWidgets.QPushButton("Cancel", self) cancel_btn.setToolTip("Cancel deletion of the project") - confirm_btn = _SameSizeBtns("Delete", self) + confirm_btn = QtWidgets.QPushButton("Permanently Delete Project", self) + confirm_btn.setObjectName("DeleteButton") confirm_btn.setEnabled(False) confirm_btn.setToolTip("Confirm deletion") From 857d22690deb8bc95df8bef3d1c2bf164beeb6b9 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 7 Jan 2022 16:36:50 +0100 Subject: [PATCH 424/492] added warning pixmap into dialog --- .../project_manager/widgets.py | 87 +++++++++++-------- 1 file changed, 49 insertions(+), 38 deletions(-) diff --git a/openpype/tools/project_manager/project_manager/widgets.py b/openpype/tools/project_manager/project_manager/widgets.py index 20a6955d817..45599ab7479 100644 --- a/openpype/tools/project_manager/project_manager/widgets.py +++ b/openpype/tools/project_manager/project_manager/widgets.py @@ -4,6 +4,7 @@ NAME_ALLOWED_SYMBOLS, NAME_REGEX ) +from .style import ResourceCache from openpype.lib import ( create_project, PROJECT_NAME_ALLOWED_SYMBOLS, @@ -13,7 +14,7 @@ from openpype.tools.utils import PlaceholderLineEdit from avalon.api import AvalonMongoDB -from Qt import QtWidgets, QtCore +from Qt import QtWidgets, QtCore, QtGui class NameTextEdit(QtWidgets.QLineEdit): @@ -291,42 +292,41 @@ def _get_existing_projects(self): return project_names, project_codes -class _SameSizeBtns(QtWidgets.QPushButton): - """Button that keep width of all button added as related. +# TODO PixmapLabel should be moved to 'utils' in other future PR so should be +# imported from there +class PixmapLabel(QtWidgets.QLabel): + """Label resizing image to height of font.""" + def __init__(self, pixmap, parent): + super(PixmapLabel, self).__init__(parent) + self._empty_pixmap = QtGui.QPixmap(0, 0) + self._source_pixmap = pixmap - This happens without changing min/max/fix size of button. Which is - welcomed for multidisplay desktops with different resolution. - """ - def __init__(self, *args, **kwargs): - super(_SameSizeBtns, self).__init__(*args, **kwargs) - self._related_btns = [] - - def add_related_btn(self, btn): - """Add related button which should be checked for width. - - Args: - btn (_SameSizeBtns): Other object of _SameSizeBtns. - """ - self._related_btns.append(btn) + def set_source_pixmap(self, pixmap): + """Change source image.""" + self._source_pixmap = pixmap + self._set_resized_pix() - def hint_width(self): - """Get size hint of button not related to others.""" - return super(_SameSizeBtns, self).sizeHint().width() + def _get_pix_size(self): + size = self.fontMetrics().height() * 4 + return size, size - def sizeHint(self): - """Calculate size hint based on size hint of this button and related. - - If width is lower than any other button it is changed to higher. - """ - result = super(_SameSizeBtns, self).sizeHint() - width = result.width() - for btn in self._related_btns: - btn_width = btn.hint_width() - if btn_width > width: - width = btn_width + def _set_resized_pix(self): + if self._source_pixmap is None: + self.setPixmap(self._empty_pixmap) + return + width, height = self._get_pix_size() + self.setPixmap( + self._source_pixmap.scaled( + width, + height, + QtCore.Qt.KeepAspectRatio, + QtCore.Qt.SmoothTransformation + ) + ) - result.setWidth(width) - return result + def resizeEvent(self, event): + self._set_resized_pix() + super(PixmapLabel, self).resizeEvent(event) class ConfirmProjectDeletion(QtWidgets.QDialog): @@ -336,15 +336,29 @@ def __init__(self, project_name, parent): self.setWindowTitle("Delete project?") - message_label = QtWidgets.QLabel(self) + top_widget = QtWidgets.QWidget(self) + + warning_pixmap = ResourceCache.get_warning_pixmap() + warning_icon_label = PixmapLabel(warning_pixmap, top_widget) + + message_label = QtWidgets.QLabel(top_widget) message_label.setWordWrap(True) message_label.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) message_label.setText(( + "WARNING: This cannot be undone.

" "Project \"{}\" with all related data will be" " permanently removed from the database (This actions won't remove" " any files on disk)." ).format(project_name)) + top_layout = QtWidgets.QHBoxLayout(top_widget) + top_layout.setContentsMargins(0, 0, 0, 0) + top_layout.addWidget( + warning_icon_label, 0, + QtCore.Qt.AlignTop | QtCore.Qt.AlignHCenter + ) + top_layout.addWidget(message_label, 1) + question_label = QtWidgets.QLabel("Are you sure?", self) confirm_input = PlaceholderLineEdit(self) @@ -359,16 +373,13 @@ def __init__(self, project_name, parent): confirm_btn.setEnabled(False) confirm_btn.setToolTip("Confirm deletion") - cancel_btn.add_related_btn(confirm_btn) - confirm_btn.add_related_btn(cancel_btn) - btns_layout = QtWidgets.QHBoxLayout() btns_layout.addStretch(1) btns_layout.addWidget(cancel_btn, 0) btns_layout.addWidget(confirm_btn, 0) layout = QtWidgets.QVBoxLayout(self) - layout.addWidget(message_label, 0) + layout.addWidget(top_widget, 0) layout.addStretch(1) layout.addWidget(question_label, 0) layout.addWidget(confirm_input, 0) From 9913872a2a36bfac2e625e04194a994fd7f97d85 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 7 Jan 2022 16:37:37 +0100 Subject: [PATCH 425/492] flame: small fixes --- openpype/hosts/flame/api/pipeline.py | 20 +++---------------- openpype/hosts/flame/hooks/pre_flame_setup.py | 18 +++++++---------- 2 files changed, 10 insertions(+), 28 deletions(-) diff --git a/openpype/hosts/flame/api/pipeline.py b/openpype/hosts/flame/api/pipeline.py index f454c33209f..9be59990d2e 100644 --- a/openpype/hosts/flame/api/pipeline.py +++ b/openpype/hosts/flame/api/pipeline.py @@ -27,13 +27,6 @@ def install(): - from .. import ( - PUBLISH_PATH, - LOAD_PATH, - CREATE_PATH, - INVENTORY_PATH - ) - # Disable all families except for the ones we explicitly want to see family_states = [ "imagesequence", @@ -60,13 +53,6 @@ def install(): log.info("OpenPype Flame host installed ...") def uninstall(): - from .. import ( - PUBLISH_PATH, - LOAD_PATH, - CREATE_PATH, - INVENTORY_PATH - ) - pyblish.deregister_host("flame") log.info("Deregistering Flame plug-ins..") @@ -167,11 +153,11 @@ def imprint(segment, data=None): @contextlib.contextmanager def maintained_selection(): import flame - from .. import selection + from .lib import CTX # check if segment is selected - if isinstance(selection[0], flame.PySegment): - sequence = get_current_sequence(selection) + if isinstance(CTX.selection[0], flame.PySegment): + sequence = get_current_sequence(CTX.selection) try: with maintained_segment_selection(sequence): yield diff --git a/openpype/hosts/flame/hooks/pre_flame_setup.py b/openpype/hosts/flame/hooks/pre_flame_setup.py index fc6b65c958d..6c13638f35d 100644 --- a/openpype/hosts/flame/hooks/pre_flame_setup.py +++ b/openpype/hosts/flame/hooks/pre_flame_setup.py @@ -19,20 +19,17 @@ class FlamePrelaunch(PreLaunchHook): """ app_groups = ["flame"] - # todo: replace version number with avalon launch app version - flame_python_exe = os.getenv("OPENPYPE_FLAME_PYTHON_EXEC") - flame_pythonpath = os.getenv("OPENPYPE_FLAME_PYTHONPATH") - wtc_script_path = os.path.join( opflame.HOST_DIR, "api", "scripts", "wiretap_com.py") def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - + self._env = self.launch_context.env + self.flame_python_exe = self._env["OPENPYPE_FLAME_PYTHON_EXEC"] + self.flame_pythonpath = self._env["OPENPYPE_FLAME_PYTHONPATH"] self.signature = "( {} )".format(self.__class__.__name__) def execute(self): - _env = self.launch_context.env """Hook entry method.""" project_doc = self.data["project_doc"] user_name = get_openpype_username() @@ -60,9 +57,9 @@ def execute(self): data_to_script = { # from settings - "host_name": _env.get("FLAME_WIRETAP_HOSTNAME") or hostname, - "volume_name": _env.get("FLAME_WIRETAP_VOLUME"), - "group_name": _env.get("FLAME_WIRETAP_GROUP"), + "host_name": self._env.get("FLAME_WIRETAP_HOSTNAME") or hostname, + "volume_name": self._env.get("FLAME_WIRETAP_VOLUME"), + "group_name": self._env.get("FLAME_WIRETAP_GROUP"), "color_policy": "ACES 1.1", # from project @@ -71,7 +68,7 @@ def execute(self): "project_data": project_data } - self.log.info(pformat(dict(_env))) + self.log.info(pformat(dict(self._env))) self.log.info(pformat(data_to_script)) # add to python path from settings @@ -92,7 +89,6 @@ def _add_pythonpath(self): self.launch_context.env["PYTHONPATH"] = os.pathsep.join(new_pythonpath) - def _get_launch_arguments(self, script_data): # Dump data to string dumped_script_data = json.dumps(script_data) From c08ad5a4cba31a37493ae041ece3a8781f20973d Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 7 Jan 2022 16:43:13 +0100 Subject: [PATCH 426/492] flame: adding Spacer class back to plugin creator gui --- openpype/hosts/flame/api/plugin.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index 1ae62f3a8d5..30a4f3dfc4c 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -37,7 +37,7 @@ def __init__(self, name, info, ui_inputs, parent=None): self.content_widget = [QtWidgets.QWidget(self)] top_layout = QtWidgets.QFormLayout(self.content_widget[0]) top_layout.setObjectName("ContentLayout") - top_layout.addSpacing(5) + top_layout.addWidget(Spacer(5, self)) # first add widget tag line top_layout.addWidget(QtWidgets.QLabel(info)) @@ -206,8 +206,7 @@ def populate_widgets(self, data, content_layout=None): self.content_layout[-1].setObjectName("sectionHeadline") headline = QtWidgets.QVBoxLayout(self.content_layout[-1]) - headline.addSpacing(20) - + headline.addWidget(Spacer(20, self)) headline.addWidget(QtWidgets.QLabel(v["label"])) # adding nested layout with label @@ -229,7 +228,7 @@ def populate_widgets(self, data, content_layout=None): self.content_layout[-1].setObjectName("sectionHeadline") headline = QtWidgets.QVBoxLayout(self.content_layout[-1]) - headline.addSpacing(20) + headline.addWidget(Spacer(20, self)) headline.addWidget(QtWidgets.QLabel(v["label"])) # adding nested layout with label @@ -265,6 +264,23 @@ def populate_widgets(self, data, content_layout=None): return data +class Spacer(QtWidgets.QWidget): + def __init__(self, height, *args, **kwargs): + super(self.__class__, self).__init__(*args, **kwargs) + + self.setFixedHeight(height) + + real_spacer = QtWidgets.QWidget(self) + real_spacer.setObjectName("Spacer") + real_spacer.setFixedHeight(height) + + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.addWidget(real_spacer) + + self.setLayout(layout) + + class Creator(openpype.Creator): """Creator class wrapper """ From 7baf6437f323a8b408504f51ff78bbb0e35bb499 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 7 Jan 2022 16:53:09 +0100 Subject: [PATCH 427/492] flame: env not resolving when discovery --- openpype/hosts/flame/hooks/pre_flame_setup.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/openpype/hosts/flame/hooks/pre_flame_setup.py b/openpype/hosts/flame/hooks/pre_flame_setup.py index 6c13638f35d..d5ddafde0cd 100644 --- a/openpype/hosts/flame/hooks/pre_flame_setup.py +++ b/openpype/hosts/flame/hooks/pre_flame_setup.py @@ -24,12 +24,13 @@ class FlamePrelaunch(PreLaunchHook): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self._env = self.launch_context.env - self.flame_python_exe = self._env["OPENPYPE_FLAME_PYTHON_EXEC"] - self.flame_pythonpath = self._env["OPENPYPE_FLAME_PYTHONPATH"] self.signature = "( {} )".format(self.__class__.__name__) def execute(self): + _env = self.launch_context.env + self.flame_python_exe = _env["OPENPYPE_FLAME_PYTHON_EXEC"] + self.flame_pythonpath = _env["OPENPYPE_FLAME_PYTHONPATH"] + """Hook entry method.""" project_doc = self.data["project_doc"] user_name = get_openpype_username() @@ -57,9 +58,9 @@ def execute(self): data_to_script = { # from settings - "host_name": self._env.get("FLAME_WIRETAP_HOSTNAME") or hostname, - "volume_name": self._env.get("FLAME_WIRETAP_VOLUME"), - "group_name": self._env.get("FLAME_WIRETAP_GROUP"), + "host_name": _env.get("FLAME_WIRETAP_HOSTNAME") or hostname, + "volume_name": _env.get("FLAME_WIRETAP_VOLUME"), + "group_name": _env.get("FLAME_WIRETAP_GROUP"), "color_policy": "ACES 1.1", # from project @@ -68,7 +69,7 @@ def execute(self): "project_data": project_data } - self.log.info(pformat(dict(self._env))) + self.log.info(pformat(dict(_env))) self.log.info(pformat(data_to_script)) # add to python path from settings From a271e05e8c8f30fcb0f98176e6a456f9fb09a3d8 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 7 Jan 2022 16:55:33 +0100 Subject: [PATCH 428/492] flame: reduction of project menu items --- openpype/hosts/flame/api/menu.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/openpype/hosts/flame/api/menu.py b/openpype/hosts/flame/api/menu.py index c4a18496d30..edb71dd1184 100644 --- a/openpype/hosts/flame/api/menu.py +++ b/openpype/hosts/flame/api/menu.py @@ -111,16 +111,6 @@ def build_menu(self): "name": "Workfiles ...", "execute": lambda x: self.tools_helper.show_workfiles() }) - menu['actions'].append({ - "name": "Create ...", - "execute": lambda x: callback_selection( - x, self.tools_helper.show_creator) - }) - menu['actions'].append({ - "name": "Publish ...", - "execute": lambda x: callback_selection( - x, self.tools_helper.show_publish) - }) menu['actions'].append({ "name": "Load ...", "execute": lambda x: self.tools_helper.show_loader() From 64445d8d21e8209e8894027664cc4770c19cc0aa Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 7 Jan 2022 17:32:18 +0100 Subject: [PATCH 429/492] flame: creator debuging --- openpype/hosts/flame/api/pipeline.py | 12 +++++++++--- openpype/hosts/flame/api/plugin.py | 16 +++++++++++----- .../flame/plugins/create/create_shot_clip.py | 1 + 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/flame/api/pipeline.py b/openpype/hosts/flame/api/pipeline.py index 9be59990d2e..b65c85f5df5 100644 --- a/openpype/hosts/flame/api/pipeline.py +++ b/openpype/hosts/flame/api/pipeline.py @@ -10,7 +10,8 @@ set_segment_data_marker, set_publish_attribute, maintained_segment_selection, - get_current_sequence + get_current_sequence, + reset_segment_selection ) from .. import HOST_DIR @@ -158,8 +159,13 @@ def maintained_selection(): # check if segment is selected if isinstance(CTX.selection[0], flame.PySegment): sequence = get_current_sequence(CTX.selection) + try: - with maintained_segment_selection(sequence): + with maintained_segment_selection(sequence) as selected: yield finally: - pass + # reset all selected clips + reset_segment_selection(sequence) + # select only original selection of segments + for segment in selected: + segment.selected = True diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index 30a4f3dfc4c..f2e67749f2e 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -89,7 +89,8 @@ def __init__(self, name, info, ui_inputs, parent=None): self.setStyleSheet(style.load_stylesheet()) def _on_ok_clicked(self): - self.result = self.value(self.items) + log.debug("ok is clicked: {}".format(self.items)) + self.result = self._values(self.items) self.close() def _on_cancel_clicked(self): @@ -100,7 +101,7 @@ def closeEvent(self, event): self.result = None event.accept() - def value(self, data, new_data=None): + def _values(self, data, new_data=None): new_data = new_data or dict() for k, v in data.items(): new_data[k] = { @@ -109,10 +110,10 @@ def value(self, data, new_data=None): } if v["type"] == "dict": new_data[k]["target"] = v["target"] - new_data[k]["value"] = self.value(v["value"]) + new_data[k]["value"] = self._values(v["value"]) if v["type"] == "section": new_data.pop(k) - new_data = self.value(v["value"], new_data) + new_data = self._values(v["value"], new_data) elif getattr(v["value"], "currentText", None): new_data[k]["target"] = v["target"] new_data[k]["value"] = v["value"].currentText() @@ -343,6 +344,8 @@ class PublishableClip: def __init__(self, segment, **kwargs): self.rename_index = kwargs["rename_index"] + self.log = kwargs["log"] + # get main parent objects self.current_segment = segment sequence_name = flib.get_current_sequence([segment]).name.get_value() @@ -369,6 +372,9 @@ def __init__(self, segment, **kwargs): # adding ui inputs if any self.ui_inputs = kwargs.get("ui_inputs", {}) + self.log.info("Inside of plugin: {}".format( + self.marker_data + )) # populate default data before we get other attributes self._populate_segment_default_data() @@ -430,7 +436,7 @@ def _populate_attributes(self): # define ui inputs if non gui mode was used self.shot_num = self.cs_index - log.debug( + self.log.debug( "____ self.shot_num: {}".format(self.shot_num)) # ui_inputs data or default values if gui was not used diff --git a/openpype/hosts/flame/plugins/create/create_shot_clip.py b/openpype/hosts/flame/plugins/create/create_shot_clip.py index 866b5108fa9..123a1c15755 100644 --- a/openpype/hosts/flame/plugins/create/create_shot_clip.py +++ b/openpype/hosts/flame/plugins/create/create_shot_clip.py @@ -61,6 +61,7 @@ def process(self): sorted_selected_segments.extend(unsorted_selected_segments) kwargs = { + "log": self.log, "ui_inputs": widget.result, "avalon": self.data } From 4097a5c0a603b9bfc10e2a500176f0a5e3b90e59 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 7 Jan 2022 18:00:51 +0100 Subject: [PATCH 430/492] flame: better selection print --- openpype/hosts/flame/api/menu.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/flame/api/menu.py b/openpype/hosts/flame/api/menu.py index edb71dd1184..b7a94e7866b 100644 --- a/openpype/hosts/flame/api/menu.py +++ b/openpype/hosts/flame/api/menu.py @@ -29,7 +29,9 @@ def callback_selection(selection, function): import openpype.hosts.flame.api as opfapi opfapi.CTX.selection = selection print("Hook Selection: \n\t{}".format( - pformat({type(item): item.name for item in opfapi.CTX.selection}) + pformat({ + index: (type(item), item.name) + for index, item in enumerate(opfapi.CTX.selection)}) )) function() From f306de1faa9d04fbe1fbfd64c290e3fdc7039cc1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 7 Jan 2022 21:29:23 +0100 Subject: [PATCH 431/492] cosmetic changes in label --- openpype/tools/project_manager/project_manager/widgets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/tools/project_manager/project_manager/widgets.py b/openpype/tools/project_manager/project_manager/widgets.py index 45599ab7479..4b5aca35ef7 100644 --- a/openpype/tools/project_manager/project_manager/widgets.py +++ b/openpype/tools/project_manager/project_manager/widgets.py @@ -347,8 +347,8 @@ def __init__(self, project_name, parent): message_label.setText(( "WARNING: This cannot be undone.

" "Project \"{}\" with all related data will be" - " permanently removed from the database (This actions won't remove" - " any files on disk)." + " permanently removed from the database. (This action won't remove" + " any files on disk.)" ).format(project_name)) top_layout = QtWidgets.QHBoxLayout(top_widget) From 2a67e7bb272b166a258819684cd4932007c2bdf0 Mon Sep 17 00:00:00 2001 From: OpenPype Date: Sat, 8 Jan 2022 03:43:20 +0000 Subject: [PATCH 432/492] [Automated] Bump version --- CHANGELOG.md | 16 ++++++++++++++-- openpype/version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c46b1f37e12..20ab0876906 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## [3.8.0-nightly.1](https://github.com/pypeclub/OpenPype/tree/HEAD) +## [3.8.0-nightly.2](https://github.com/pypeclub/OpenPype/tree/HEAD) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.7.0...HEAD) @@ -10,6 +10,8 @@ **🚀 Enhancements** +- Ftrack: Event handlers settings [\#2496](https://github.com/pypeclub/OpenPype/pull/2496) +- Tools: Fix style and modality of errors in loader and creator [\#2489](https://github.com/pypeclub/OpenPype/pull/2489) - Tools: Be able to change models of tasks and assets widgets [\#2475](https://github.com/pypeclub/OpenPype/pull/2475) - Publish pype: Reduce publish process defering [\#2464](https://github.com/pypeclub/OpenPype/pull/2464) - Maya: Improve speed of Collect History logic [\#2460](https://github.com/pypeclub/OpenPype/pull/2460) @@ -19,8 +21,19 @@ **🐛 Bug fixes** +- General: Settings work if OpenPypeVersion is available [\#2494](https://github.com/pypeclub/OpenPype/pull/2494) +- Workfiles tool: Files widget show files on first show [\#2488](https://github.com/pypeclub/OpenPype/pull/2488) - General: Custom template paths filter fix [\#2483](https://github.com/pypeclub/OpenPype/pull/2483) - Loader: Remove always on top flag in tray [\#2480](https://github.com/pypeclub/OpenPype/pull/2480) +- General: Anatomy does not return root envs as unicode [\#2465](https://github.com/pypeclub/OpenPype/pull/2465) +- Webpublisher: Fix assignment of families of TVpaint instances [\#2373](https://github.com/pypeclub/OpenPype/pull/2373) + +**Merged pull requests:** + +- General: Modules import function output fix [\#2492](https://github.com/pypeclub/OpenPype/pull/2492) +- AE: fix hiding of alert window below Publish [\#2491](https://github.com/pypeclub/OpenPype/pull/2491) +- Maya: Validate NGONs re-use polyConstraint code from openpype.host.maya.api.lib [\#2458](https://github.com/pypeclub/OpenPype/pull/2458) +- Version handling [\#2363](https://github.com/pypeclub/OpenPype/pull/2363) ## [3.7.0](https://github.com/pypeclub/OpenPype/tree/3.7.0) (2022-01-04) @@ -67,7 +80,6 @@ - hiero: solve custom ocio path [\#2379](https://github.com/pypeclub/OpenPype/pull/2379) - hiero: fix workio and flatten [\#2378](https://github.com/pypeclub/OpenPype/pull/2378) - Nuke: fixing menu re-drawing during context change [\#2374](https://github.com/pypeclub/OpenPype/pull/2374) -- Webpublisher: Fix assignment of families of TVpaint instances [\#2373](https://github.com/pypeclub/OpenPype/pull/2373) - Nuke: fixing node name based on switched asset name [\#2369](https://github.com/pypeclub/OpenPype/pull/2369) - Houdini: Fix HDA creation [\#2350](https://github.com/pypeclub/OpenPype/pull/2350) diff --git a/openpype/version.py b/openpype/version.py index 4f10deeae15..ed0a96d4de9 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.8.0-nightly.1" +__version__ = "3.8.0-nightly.2" diff --git a/pyproject.toml b/pyproject.toml index 20aaf62d064..0ef447e0be8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.8.0-nightly.1" # OpenPype +version = "3.8.0-nightly.2" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From 5d2c5d7776b68de2b7f37a262f69e16c7f4c4071 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sun, 9 Jan 2022 20:55:14 +0100 Subject: [PATCH 433/492] Fix #2497: reset empty string attributes correctly to "" instead of "None" --- openpype/hosts/maya/api/lib.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 52ebcaff649..ac22cdc7779 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -313,13 +313,7 @@ def attribute_values(attr_values): """ - # NOTE(antirotor): this didn't work for some reason for Yeti attributes - # original = [(attr, cmds.getAttr(attr)) for attr in attr_values] - original = [] - for attr in attr_values: - type = cmds.getAttr(attr, type=True) - value = cmds.getAttr(attr) - original.append((attr, str(value) if type == "string" else value)) + original = [(attr, cmds.getAttr(attr)) for attr in attr_values] try: for attr, value in attr_values.items(): if isinstance(value, string_types): @@ -331,6 +325,12 @@ def attribute_values(attr_values): for attr, value in original: if isinstance(value, string_types): cmds.setAttr(attr, value, type="string") + elif value is None and cmds.getAttr(attr, type=True) == "string": + # In some cases the maya.cmds.getAttr command returns None + # for string attributes but this value cannot assigned. + # Note: After setting it once to "" it will then return "" + # instead of None. So this would only happen once. + cmds.setAttr(attr, "", type="string") else: cmds.setAttr(attr, value) From b66f6b95bb46dbaf671ca70e16c506943fad88be Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 10 Jan 2022 12:00:42 +0100 Subject: [PATCH 434/492] OP-2205 - working version for upload multiple files Won't be used probably as purging of old files would be impossible in this use case. --- .../plugins/publish/integrate_slack_api.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/openpype/modules/slack/plugins/publish/integrate_slack_api.py b/openpype/modules/slack/plugins/publish/integrate_slack_api.py index d7be0c0bfad..5aba3725492 100644 --- a/openpype/modules/slack/plugins/publish/integrate_slack_api.py +++ b/openpype/modules/slack/plugins/publish/integrate_slack_api.py @@ -25,7 +25,7 @@ class IntegrateSlackAPI(pyblish.api.InstancePlugin): optional = True # internal, not configurable - bot_user_name = "OpenpypeNotifier" + bot_user_name = "OpenPypeNotifier" icon_url = "https://openpype.io/img/favicon/favicon.ico" def process(self, instance): @@ -37,11 +37,12 @@ def process(self, instance): message = self._get_filled_message(message_profile["message"], instance, review_path) + self.log.info("message:: {}".format(message)) if not message: return - if message_profile["upload_thumbnail"] and thumbnail_path: - publish_files.add(thumbnail_path) + # if message_profile["upload_thumbnail"] and thumbnail_path: + # publish_files.add(thumbnail_path) if message_profile["upload_review"] and review_path: publish_files.add(review_path) @@ -130,14 +131,14 @@ def _python2_call(self, token, channel, message, from slackclient import SlackClient try: client = SlackClient(token) + self.log.info("publish {}".format(publish_files)) + attachment_str = "\n\n Attachment links: \n" for p_file in publish_files: - attachment_str = "\n\n Attachment links: \n" with open(p_file, 'rb') as pf: response = client.api_call( "files.upload", channels=channel, - file=pf, - title=os.path.basename(p_file), + file=pf ) attachment_str += "\n<{}|{}>".format( response["file"]["permalink"], @@ -149,11 +150,9 @@ def _python2_call(self, token, channel, message, response = client.api_call( "chat.postMessage", channel=channel, - text=message, - username=self.bot_user_name, - icon_url=self.icon_url + text=message ) - + self.log.info("repsonse {}".format(response)) if response.get("error"): error_str = self._enrich_error(str(response.get("error")), channel) From e268ad9de1bd6ba4a020931376b335fd7e0acf1e Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 10 Jan 2022 12:11:06 +0100 Subject: [PATCH 435/492] flame: fixing selected conditions --- openpype/hosts/flame/api/lib.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index b37cc35afd7..2cc9fee1730 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -446,8 +446,12 @@ def get_sequence_segments(sequence, selected=False): continue # loop all segment in remaining tracks for segment in track.segments: - # ignore all segments not selected - if segment.selected is not True and selected is True: + if segment.name.get_value() == "": + continue + if ( + selected is True + and segment.selected.get_value() is not True + ): continue # add it to original selection segments.append(segment) From ca693c1666aa0e69e92193aee62781bd7dc20be1 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 10 Jan 2022 12:14:39 +0100 Subject: [PATCH 436/492] flame: fixing creator plugin operation after refactory --- openpype/hosts/flame/api/plugin.py | 27 ++++++++++++++----- .../flame/plugins/create/create_shot_clip.py | 14 +++++----- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index f2e67749f2e..7432d61890c 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -17,6 +17,7 @@ class CreatorWidget(QtWidgets.QDialog): # output items items = dict() + _results_back = None def __init__(self, name, info, ui_inputs, parent=None): super(CreatorWidget, self).__init__(parent) @@ -88,18 +89,27 @@ def __init__(self, name, info, ui_inputs, parent=None): self.setStyleSheet(style.load_stylesheet()) + @classmethod + def set_results_back(cls, value): + cls._results_back = value + + @classmethod + def get_results_back(cls): + return cls._results_back + def _on_ok_clicked(self): log.debug("ok is clicked: {}".format(self.items)) - self.result = self._values(self.items) + results_back = self._values(self.items) + self.set_results_back(results_back) self.close() def _on_cancel_clicked(self): - self.result = None + self.set_results_back(None) self.close() - def closeEvent(self, event): - self.result = None - event.accept() + def showEvent(self, event): + self.set_results_back(None) + super(CreatorWidget, self).showEvent(event) def _values(self, data, new_data=None): new_data = new_data or dict() @@ -303,7 +313,9 @@ def __init__(self, *args, **kwargs): self.selected = flib.get_sequence_segments(self.sequence) def create_widget(self, *args, **kwargs): - return CreatorWidget(*args, **kwargs) + widget = CreatorWidget(*args, **kwargs) + widget.exec_() + return widget.get_results_back() class PublishableClip: @@ -344,6 +356,7 @@ class PublishableClip: def __init__(self, segment, **kwargs): self.rename_index = kwargs["rename_index"] + self.family = kwargs["family"] self.log = kwargs["log"] # get main parent objects @@ -580,7 +593,7 @@ def _solve_tag_hierarchy_data(self, hierarchy_formating_data): "hierarchyData": hierarchy_formating_data, "subset": self.subset, "family": self.subset_family, - "families": [self.data["family"]] + "families": [self.family] } def _convert_to_entity(self, type, template): diff --git a/openpype/hosts/flame/plugins/create/create_shot_clip.py b/openpype/hosts/flame/plugins/create/create_shot_clip.py index 123a1c15755..edc3e7176c3 100644 --- a/openpype/hosts/flame/plugins/create/create_shot_clip.py +++ b/openpype/hosts/flame/plugins/create/create_shot_clip.py @@ -1,8 +1,6 @@ from copy import deepcopy import openpype.hosts.flame.api as opfapi -reload(opfapi) # noqa - class CreateShotClip(opfapi.Creator): """Publishable clip""" @@ -32,22 +30,21 @@ def process(self): gui_inputs[k]["value"] = presets[k] # open widget for plugins inputs - widget = self.create_widget( + results_back = self.create_widget( "Pype publish attributes creator", "Define sequential rename and fill hierarchy data.", gui_inputs ) - widget.exec_() if len(self.selected) < 1: return - if not widget.result: + if not results_back: print("Operation aborted") return # get ui output for track name for vertical sync - v_sync_track = widget.result["vSyncTrack"]["value"] + v_sync_track = results_back["vSyncTrack"]["value"] # sort selected trackItems by sorted_selected_segments = [] @@ -62,8 +59,9 @@ def process(self): kwargs = { "log": self.log, - "ui_inputs": widget.result, - "avalon": self.data + "ui_inputs": results_back, + "avalon": self.data, + "family": self.data["family"] } for i, segment in enumerate(sorted_selected_segments): From 1454e718e7722c66c4f0d238fef1398704fc59d2 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 10 Jan 2022 12:15:15 +0100 Subject: [PATCH 437/492] flame: remove flame api reload destroying selection --- openpype/hosts/flame/plugins/publish/collect_test_selection.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/flame/plugins/publish/collect_test_selection.py b/openpype/hosts/flame/plugins/publish/collect_test_selection.py index 0431bd1fe34..0c75b3204fe 100644 --- a/openpype/hosts/flame/plugins/publish/collect_test_selection.py +++ b/openpype/hosts/flame/plugins/publish/collect_test_selection.py @@ -3,7 +3,6 @@ import openpype.hosts.flame.api as opfapi from openpype.hosts.flame.otio import flame_export as otio_export from pprint import pformat -reload(opfapi) # noqa reload(otio_export) # noqa From ba382242ce3d80d221c3622f441c0f58efb55c75 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 10 Jan 2022 12:45:05 +0100 Subject: [PATCH 438/492] flame: shot number based on segment index --- openpype/hosts/flame/api/plugin.py | 9 ++++++++- .../hosts/flame/plugins/create/create_shot_clip.py | 11 +++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index 7432d61890c..e6165a6d7e1 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -353,6 +353,7 @@ class PublishableClip: count_steps_default = 10 vertical_sync_default = False driving_layer_default = "" + index_from_segment_default = False def __init__(self, segment, **kwargs): self.rename_index = kwargs["rename_index"] @@ -462,6 +463,8 @@ def _populate_attributes(self): self.hierarchy_data = self.ui_inputs.get( "hierarchyData", {}).get("value") or \ self.current_segment_default_data.copy() + self.index_from_segment = self.ui_inputs.get( + "segmentIndex", {}).get("value") or self.index_from_segment_default self.count_from = self.ui_inputs.get( "countFrom", {}).get("value") or self.count_from_default self.count_steps = self.ui_inputs.get( @@ -524,8 +527,12 @@ def _convert_to_marker_data(self): self.review_track not in self.review_track_default): # if review layer is defined and not the same as defalut self.review_layer = self.review_track + # shot num calculate - if self.rename_index == 0: + if self.index_from_segment: + # use clip index from timeline + self.shot_num = self.count_steps * self.cs_index + elif self.rename_index == 0: self.shot_num = self.count_from else: self.shot_num = self.count_from + self.count_steps diff --git a/openpype/hosts/flame/plugins/create/create_shot_clip.py b/openpype/hosts/flame/plugins/create/create_shot_clip.py index edc3e7176c3..f055c77a898 100644 --- a/openpype/hosts/flame/plugins/create/create_shot_clip.py +++ b/openpype/hosts/flame/plugins/create/create_shot_clip.py @@ -101,20 +101,27 @@ def get_gui_inputs(self): "target": "ui", "toolTip": "template for creating shot namespaused for renaming (use rename: on)", # noqa "order": 2}, + "segmentIndex": { + "value": True, + "type": "QCheckBox", + "label": "Segment index", + "target": "ui", + "toolTip": "Take number from segment index", # noqa + "order": 3}, "countFrom": { "value": 10, "type": "QSpinBox", "label": "Count sequence from", "target": "ui", "toolTip": "Set when the sequence number stafrom", # noqa - "order": 3}, + "order": 4}, "countSteps": { "value": 10, "type": "QSpinBox", "label": "Stepping number", "target": "ui", "toolTip": "What number is adding every new step", # noqa - "order": 4}, + "order": 5}, } }, "hierarchyData": { From 4f612a6a169123ea9918d2f2bbb1e3816bc4b07e Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 10 Jan 2022 12:54:55 +0100 Subject: [PATCH 439/492] flame: improving previous commit --- openpype/hosts/flame/api/plugin.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index e6165a6d7e1..f34999bcf3f 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -509,7 +509,8 @@ def _convert_to_marker_data(self): hero_track = False # increasing steps by index of rename iteration - self.count_steps *= self.rename_index + if not self.index_from_segment: + self.count_steps *= self.rename_index hierarchy_formating_data = {} hierarchy_data = deepcopy(self.hierarchy_data) @@ -532,10 +533,11 @@ def _convert_to_marker_data(self): if self.index_from_segment: # use clip index from timeline self.shot_num = self.count_steps * self.cs_index - elif self.rename_index == 0: - self.shot_num = self.count_from else: - self.shot_num = self.count_from + self.count_steps + if self.rename_index == 0: + self.shot_num = self.count_from + else: + self.shot_num = self.count_from + self.count_steps # clip name sequence number _data.update({"shot": self.shot_num}) From 2a0f7b48d99d95219db0816b23679e965178cdf2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 10 Jan 2022 14:06:12 +0100 Subject: [PATCH 440/492] fix how run_openpype_process works --- openpype/lib/execute.py | 2 +- openpype/plugins/publish/extract_burnin.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/lib/execute.py b/openpype/lib/execute.py index 16b98eefb48..3cf67a379c3 100644 --- a/openpype/lib/execute.py +++ b/openpype/lib/execute.py @@ -164,7 +164,7 @@ def run_openpype_process(*args, **kwargs): Example: ``` - run_openpype_process(["run", ""]) + run_openpype_process("run", "") ``` Args: diff --git a/openpype/plugins/publish/extract_burnin.py b/openpype/plugins/publish/extract_burnin.py index 1cb8608a563..459c66ee432 100644 --- a/openpype/plugins/publish/extract_burnin.py +++ b/openpype/plugins/publish/extract_burnin.py @@ -312,7 +312,7 @@ def main_process(self, instance): if platform.system().lower() == "windows": process_kwargs["creationflags"] = CREATE_NO_WINDOW - run_openpype_process(args, **process_kwargs) + run_openpype_process(*args, **process_kwargs) # Remove the temporary json os.remove(temporary_json_filepath) From e94aa5311651c3219c5afe8219b0c5ecaa35e4c7 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 10 Jan 2022 23:00:45 +0100 Subject: [PATCH 441/492] moved photoshop from avalon to openpype --- openpype/hosts/photoshop/api/README.md | 255 ++++ openpype/hosts/photoshop/api/__init__.py | 134 +- openpype/hosts/photoshop/api/extension.zxp | Bin 0 -> 54111 bytes openpype/hosts/photoshop/api/extension/.debug | 9 + .../photoshop/api/extension/CSXS/manifest.xml | 53 + .../api/extension/client/CSInterface.js | 1193 +++++++++++++++++ .../photoshop/api/extension/client/client.js | 300 +++++ .../api/extension/client/loglevel.min.js | 2 + .../photoshop/api/extension/client/wsrpc.js | 393 ++++++ .../api/extension/client/wsrpc.min.js | 1 + .../hosts/photoshop/api/extension/host/JSX.js | 774 +++++++++++ .../photoshop/api/extension/host/index.jsx | 484 +++++++ .../photoshop/api/extension/host/json.js | 530 ++++++++ .../api/extension/icons/avalon-logo-48.png | Bin 0 -> 1362 bytes .../hosts/photoshop/api/extension/index.html | 119 ++ openpype/hosts/photoshop/api/launch_logic.py | 315 +++++ openpype/hosts/photoshop/api/lib.py | 76 ++ openpype/hosts/photoshop/api/panel.PNG | Bin 0 -> 8756 bytes .../hosts/photoshop/api/panel_failure.PNG | Bin 0 -> 13568 bytes openpype/hosts/photoshop/api/pipeline.py | 199 +++ openpype/hosts/photoshop/api/workio.py | 50 + openpype/hosts/photoshop/api/ws_stub.py | 470 +++++++ 22 files changed, 5278 insertions(+), 79 deletions(-) create mode 100644 openpype/hosts/photoshop/api/README.md create mode 100644 openpype/hosts/photoshop/api/extension.zxp create mode 100644 openpype/hosts/photoshop/api/extension/.debug create mode 100644 openpype/hosts/photoshop/api/extension/CSXS/manifest.xml create mode 100644 openpype/hosts/photoshop/api/extension/client/CSInterface.js create mode 100644 openpype/hosts/photoshop/api/extension/client/client.js create mode 100644 openpype/hosts/photoshop/api/extension/client/loglevel.min.js create mode 100644 openpype/hosts/photoshop/api/extension/client/wsrpc.js create mode 100644 openpype/hosts/photoshop/api/extension/client/wsrpc.min.js create mode 100644 openpype/hosts/photoshop/api/extension/host/JSX.js create mode 100644 openpype/hosts/photoshop/api/extension/host/index.jsx create mode 100644 openpype/hosts/photoshop/api/extension/host/json.js create mode 100644 openpype/hosts/photoshop/api/extension/icons/avalon-logo-48.png create mode 100644 openpype/hosts/photoshop/api/extension/index.html create mode 100644 openpype/hosts/photoshop/api/launch_logic.py create mode 100644 openpype/hosts/photoshop/api/lib.py create mode 100644 openpype/hosts/photoshop/api/panel.PNG create mode 100644 openpype/hosts/photoshop/api/panel_failure.PNG create mode 100644 openpype/hosts/photoshop/api/pipeline.py create mode 100644 openpype/hosts/photoshop/api/workio.py create mode 100644 openpype/hosts/photoshop/api/ws_stub.py diff --git a/openpype/hosts/photoshop/api/README.md b/openpype/hosts/photoshop/api/README.md new file mode 100644 index 00000000000..b958f538033 --- /dev/null +++ b/openpype/hosts/photoshop/api/README.md @@ -0,0 +1,255 @@ +# Photoshop Integration + +## Setup + +The Photoshop integration requires two components to work; `extension` and `server`. + +### Extension + +To install the extension download [Extension Manager Command Line tool (ExManCmd)](https://github.com/Adobe-CEP/Getting-Started-guides/tree/master/Package%20Distribute%20Install#option-2---exmancmd). + +``` +ExManCmd /install {path to avalon-core}\avalon\photoshop\extension.zxp +``` + +### Server + +The easiest way to get the server and Photoshop launch is with: + +``` +python -c ^"import avalon.photoshop;avalon.photoshop.launch(""C:\Program Files\Adobe\Adobe Photoshop 2020\Photoshop.exe"")^" +``` + +`avalon.photoshop.launch` launches the application and server, and also closes the server when Photoshop exists. + +## Usage + +The Photoshop extension can be found under `Window > Extensions > Avalon`. Once launched you should be presented with a panel like this: + +![Avalon Panel](panel.PNG "Avalon Panel") + + +## Developing + +### Extension +When developing the extension you can load it [unsigned](https://github.com/Adobe-CEP/CEP-Resources/blob/master/CEP_9.x/Documentation/CEP%209.0%20HTML%20Extension%20Cookbook.md#debugging-unsigned-extensions). + +When signing the extension you can use this [guide](https://github.com/Adobe-CEP/Getting-Started-guides/tree/master/Package%20Distribute%20Install#package-distribute-install-guide). + +``` +ZXPSignCmd -selfSignedCert NA NA Avalon Avalon-Photoshop avalon extension.p12 +ZXPSignCmd -sign {path to avalon-core}\avalon\photoshop\extension {path to avalon-core}\avalon\photoshop\extension.zxp extension.p12 avalon +``` + +### Plugin Examples + +These plugins were made with the [polly config](https://github.com/mindbender-studio/config). To fully integrate and load, you will have to use this config and add `image` to the [integration plugin](https://github.com/mindbender-studio/config/blob/master/polly/plugins/publish/integrate_asset.py). + +#### Creator Plugin +```python +from avalon import photoshop + + +class CreateImage(photoshop.Creator): + """Image folder for publish.""" + + name = "imageDefault" + label = "Image" + family = "image" + + def __init__(self, *args, **kwargs): + super(CreateImage, self).__init__(*args, **kwargs) +``` + +#### Collector Plugin +```python +import pythoncom + +import pyblish.api + + +class CollectInstances(pyblish.api.ContextPlugin): + """Gather instances by LayerSet and file metadata + + This collector takes into account assets that are associated with + an LayerSet and marked with a unique identifier; + + Identifier: + id (str): "pyblish.avalon.instance" + """ + + label = "Instances" + order = pyblish.api.CollectorOrder + hosts = ["photoshop"] + families_mapping = { + "image": [] + } + + def process(self, context): + # Necessary call when running in a different thread which pyblish-qml + # can be. + pythoncom.CoInitialize() + + photoshop_client = PhotoshopClientStub() + layers = photoshop_client.get_layers() + layers_meta = photoshop_client.get_layers_metadata() + for layer in layers: + layer_data = photoshop_client.read(layer, layers_meta) + + # Skip layers without metadata. + if layer_data is None: + continue + + # Skip containers. + if "container" in layer_data["id"]: + continue + + # child_layers = [*layer.Layers] + # self.log.debug("child_layers {}".format(child_layers)) + # if not child_layers: + # self.log.info("%s skipped, it was empty." % layer.Name) + # continue + + instance = context.create_instance(layer.name) + instance.append(layer) + instance.data.update(layer_data) + instance.data["families"] = self.families_mapping[ + layer_data["family"] + ] + instance.data["publish"] = layer.visible + + # Produce diagnostic message for any graphical + # user interface interested in visualising it. + self.log.info("Found: \"%s\" " % instance.data["name"]) +``` + +#### Extractor Plugin +```python +import os + +import openpype.api +from avalon import photoshop + + +class ExtractImage(openpype.api.Extractor): + """Produce a flattened image file from instance + + This plug-in takes into account only the layers in the group. + """ + + label = "Extract Image" + hosts = ["photoshop"] + families = ["image"] + formats = ["png", "jpg"] + + def process(self, instance): + + staging_dir = self.staging_dir(instance) + self.log.info("Outputting image to {}".format(staging_dir)) + + # Perform extraction + stub = photoshop.stub() + files = {} + with photoshop.maintained_selection(): + self.log.info("Extracting %s" % str(list(instance))) + with photoshop.maintained_visibility(): + # Hide all other layers. + extract_ids = set([ll.id for ll in stub. + get_layers_in_layers([instance[0]])]) + + for layer in stub.get_layers(): + # limit unnecessary calls to client + if layer.visible and layer.id not in extract_ids: + stub.set_visible(layer.id, False) + + save_options = [] + if "png" in self.formats: + save_options.append('png') + if "jpg" in self.formats: + save_options.append('jpg') + + file_basename = os.path.splitext( + stub.get_active_document_name() + )[0] + for extension in save_options: + _filename = "{}.{}".format(file_basename, extension) + files[extension] = _filename + + full_filename = os.path.join(staging_dir, _filename) + stub.saveAs(full_filename, extension, True) + + representations = [] + for extension, filename in files.items(): + representations.append({ + "name": extension, + "ext": extension, + "files": filename, + "stagingDir": staging_dir + }) + instance.data["representations"] = representations + instance.data["stagingDir"] = staging_dir + + self.log.info(f"Extracted {instance} to {staging_dir}") +``` + +#### Loader Plugin +```python +from avalon import api, photoshop + +stub = photoshop.stub() + + +class ImageLoader(api.Loader): + """Load images + + Stores the imported asset in a container named after the asset. + """ + + families = ["image"] + representations = ["*"] + + def load(self, context, name=None, namespace=None, data=None): + with photoshop.maintained_selection(): + layer = stub.import_smart_object(self.fname) + + self[:] = [layer] + + return photoshop.containerise( + name, + namespace, + layer, + context, + self.__class__.__name__ + ) + + def update(self, container, representation): + layer = container.pop("layer") + + with photoshop.maintained_selection(): + stub.replace_smart_object( + layer, api.get_representation_path(representation) + ) + + stub.imprint( + layer, {"representation": str(representation["_id"])} + ) + + def remove(self, container): + container["layer"].Delete() + + def switch(self, container, representation): + self.update(container, representation) +``` +For easier debugging of Javascript: +https://community.adobe.com/t5/download-install/adobe-extension-debuger-problem/td-p/10911704?page=1 +Add --enable-blink-features=ShadowDOMV0,CustomElementsV0 when starting Chrome +then localhost:8078 (port set in `photoshop\extension\.debug`) + +Or use Visual Studio Code https://medium.com/adobetech/extendscript-debugger-for-visual-studio-code-public-release-a2ff6161fa01 + +Or install CEF client from https://github.com/Adobe-CEP/CEP-Resources/tree/master/CEP_9.x +## Resources + - https://github.com/lohriialo/photoshop-scripting-python + - https://www.adobe.com/devnet/photoshop/scripting.html + - https://github.com/Adobe-CEP/Getting-Started-guides + - https://github.com/Adobe-CEP/CEP-Resources diff --git a/openpype/hosts/photoshop/api/__init__.py b/openpype/hosts/photoshop/api/__init__.py index d978d6ecc18..43756b9ee4f 100644 --- a/openpype/hosts/photoshop/api/__init__.py +++ b/openpype/hosts/photoshop/api/__init__.py @@ -1,79 +1,55 @@ -import os -import sys -import logging - -from Qt import QtWidgets - -from avalon import io -from avalon import api as avalon -from openpype import lib -from pyblish import api as pyblish -import openpype.hosts.photoshop - -log = logging.getLogger("openpype.hosts.photoshop") - -HOST_DIR = os.path.dirname(os.path.abspath(openpype.hosts.photoshop.__file__)) -PLUGINS_DIR = os.path.join(HOST_DIR, "plugins") -PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish") -LOAD_PATH = os.path.join(PLUGINS_DIR, "load") -CREATE_PATH = os.path.join(PLUGINS_DIR, "create") -INVENTORY_PATH = os.path.join(PLUGINS_DIR, "inventory") - -def check_inventory(): - if not lib.any_outdated(): - return - - host = avalon.registered_host() - outdated_containers = [] - for container in host.ls(): - representation = container['representation'] - representation_doc = io.find_one( - { - "_id": io.ObjectId(representation), - "type": "representation" - }, - projection={"parent": True} - ) - if representation_doc and not lib.is_latest(representation_doc): - outdated_containers.append(container) - - # Warn about outdated containers. - print("Starting new QApplication..") - app = QtWidgets.QApplication(sys.argv) - - message_box = QtWidgets.QMessageBox() - message_box.setIcon(QtWidgets.QMessageBox.Warning) - msg = "There are outdated containers in the scene." - message_box.setText(msg) - message_box.exec_() - - # Garbage collect QApplication. - del app - - -def application_launch(): - check_inventory() - - -def install(): - print("Installing Pype config...") - - pyblish.register_plugin_path(PUBLISH_PATH) - avalon.register_plugin_path(avalon.Loader, LOAD_PATH) - avalon.register_plugin_path(avalon.Creator, CREATE_PATH) - log.info(PUBLISH_PATH) - - pyblish.register_callback( - "instanceToggled", on_pyblish_instance_toggled - ) - - avalon.on("application.launched", application_launch) - -def uninstall(): - pyblish.deregister_plugin_path(PUBLISH_PATH) - avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH) - avalon.deregister_plugin_path(avalon.Creator, CREATE_PATH) - -def on_pyblish_instance_toggled(instance, old_value, new_value): - """Toggle layer visibility on instance toggles.""" - instance[0].Visible = new_value +"""Public API + +Anything that isn't defined here is INTERNAL and unreliable for external use. + +""" + +from .pipeline import ( + ls, + list_instances, + remove_instance, + Creator, + install, + containerise +) + +from .workio import ( + file_extensions, + has_unsaved_changes, + save_file, + open_file, + current_file, + work_root, +) + +from .lib import ( + maintained_selection, + maintained_visibility +) + +from .launch_logic import stub + +__all__ = [ + # pipeline + "ls", + "list_instances", + "remove_instance", + "Creator", + "install", + "containerise", + + # workfiles + "file_extensions", + "has_unsaved_changes", + "save_file", + "open_file", + "current_file", + "work_root", + + # lib + "maintained_selection", + "maintained_visibility", + + # launch_logic + "stub" +] diff --git a/openpype/hosts/photoshop/api/extension.zxp b/openpype/hosts/photoshop/api/extension.zxp new file mode 100644 index 0000000000000000000000000000000000000000..a25ec96e7df2348e558277faedf4f74561a7f316 GIT binary patch literal 54111 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� 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¨9*zdQ|q^=Mf~ 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 z + + + + + + + + \ No newline at end of file diff --git a/openpype/hosts/photoshop/api/extension/CSXS/manifest.xml b/openpype/hosts/photoshop/api/extension/CSXS/manifest.xml new file mode 100644 index 00000000000..6396cd24122 --- /dev/null +++ b/openpype/hosts/photoshop/api/extension/CSXS/manifest.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + ./index.html + + + + true + + + applicationActivate + com.adobe.csxs.events.ApplicationInitialized + + + + Panel +

OpenPype + + + 300 + 140 + + + 400 + 200 + + + + ./icons/avalon-logo-48.png + + + + + + diff --git a/openpype/hosts/photoshop/api/extension/client/CSInterface.js b/openpype/hosts/photoshop/api/extension/client/CSInterface.js new file mode 100644 index 00000000000..4239391efd0 --- /dev/null +++ b/openpype/hosts/photoshop/api/extension/client/CSInterface.js @@ -0,0 +1,1193 @@ +/************************************************************************************************** +* +* ADOBE SYSTEMS INCORPORATED +* Copyright 2013 Adobe Systems Incorporated +* All Rights Reserved. +* +* NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the +* terms of the Adobe license agreement accompanying it. If you have received this file from a +* source other than Adobe, then your use, modification, or distribution of it requires the prior +* written permission of Adobe. +* +**************************************************************************************************/ + +/** CSInterface - v8.0.0 */ + +/** + * Stores constants for the window types supported by the CSXS infrastructure. + */ +function CSXSWindowType() +{ +} + +/** Constant for the CSXS window type Panel. */ +CSXSWindowType._PANEL = "Panel"; + +/** Constant for the CSXS window type Modeless. */ +CSXSWindowType._MODELESS = "Modeless"; + +/** Constant for the CSXS window type ModalDialog. */ +CSXSWindowType._MODAL_DIALOG = "ModalDialog"; + +/** EvalScript error message */ +EvalScript_ErrMessage = "EvalScript error."; + +/** + * @class Version + * Defines a version number with major, minor, micro, and special + * components. The major, minor and micro values are numeric; the special + * value can be any string. + * + * @param major The major version component, a positive integer up to nine digits long. + * @param minor The minor version component, a positive integer up to nine digits long. + * @param micro The micro version component, a positive integer up to nine digits long. + * @param special The special version component, an arbitrary string. + * + * @return A new \c Version object. + */ +function Version(major, minor, micro, special) +{ + this.major = major; + this.minor = minor; + this.micro = micro; + this.special = special; +} + +/** + * The maximum value allowed for a numeric version component. + * This reflects the maximum value allowed in PlugPlug and the manifest schema. + */ +Version.MAX_NUM = 999999999; + +/** + * @class VersionBound + * Defines a boundary for a version range, which associates a \c Version object + * with a flag for whether it is an inclusive or exclusive boundary. + * + * @param version The \c #Version object. + * @param inclusive True if this boundary is inclusive, false if it is exclusive. + * + * @return A new \c VersionBound object. + */ +function VersionBound(version, inclusive) +{ + this.version = version; + this.inclusive = inclusive; +} + +/** + * @class VersionRange + * Defines a range of versions using a lower boundary and optional upper boundary. + * + * @param lowerBound The \c #VersionBound object. + * @param upperBound The \c #VersionBound object, or null for a range with no upper boundary. + * + * @return A new \c VersionRange object. + */ +function VersionRange(lowerBound, upperBound) +{ + this.lowerBound = lowerBound; + this.upperBound = upperBound; +} + +/** + * @class Runtime + * Represents a runtime related to the CEP infrastructure. + * Extensions can declare dependencies on particular + * CEP runtime versions in the extension manifest. + * + * @param name The runtime name. + * @param version A \c #VersionRange object that defines a range of valid versions. + * + * @return A new \c Runtime object. + */ +function Runtime(name, versionRange) +{ + this.name = name; + this.versionRange = versionRange; +} + +/** +* @class Extension +* Encapsulates a CEP-based extension to an Adobe application. +* +* @param id The unique identifier of this extension. +* @param name The localizable display name of this extension. +* @param mainPath The path of the "index.html" file. +* @param basePath The base path of this extension. +* @param windowType The window type of the main window of this extension. + Valid values are defined by \c #CSXSWindowType. +* @param width The default width in pixels of the main window of this extension. +* @param height The default height in pixels of the main window of this extension. +* @param minWidth The minimum width in pixels of the main window of this extension. +* @param minHeight The minimum height in pixels of the main window of this extension. +* @param maxWidth The maximum width in pixels of the main window of this extension. +* @param maxHeight The maximum height in pixels of the main window of this extension. +* @param defaultExtensionDataXml The extension data contained in the default \c ExtensionDispatchInfo section of the extension manifest. +* @param specialExtensionDataXml The extension data contained in the application-specific \c ExtensionDispatchInfo section of the extension manifest. +* @param requiredRuntimeList An array of \c Runtime objects for runtimes required by this extension. +* @param isAutoVisible True if this extension is visible on loading. +* @param isPluginExtension True if this extension has been deployed in the Plugins folder of the host application. +* +* @return A new \c Extension object. +*/ +function Extension(id, name, mainPath, basePath, windowType, width, height, minWidth, minHeight, maxWidth, maxHeight, + defaultExtensionDataXml, specialExtensionDataXml, requiredRuntimeList, isAutoVisible, isPluginExtension) +{ + this.id = id; + this.name = name; + this.mainPath = mainPath; + this.basePath = basePath; + this.windowType = windowType; + this.width = width; + this.height = height; + this.minWidth = minWidth; + this.minHeight = minHeight; + this.maxWidth = maxWidth; + this.maxHeight = maxHeight; + this.defaultExtensionDataXml = defaultExtensionDataXml; + this.specialExtensionDataXml = specialExtensionDataXml; + this.requiredRuntimeList = requiredRuntimeList; + this.isAutoVisible = isAutoVisible; + this.isPluginExtension = isPluginExtension; +} + +/** + * @class CSEvent + * A standard JavaScript event, the base class for CEP events. + * + * @param type The name of the event type. + * @param scope The scope of event, can be "GLOBAL" or "APPLICATION". + * @param appId The unique identifier of the application that generated the event. + * @param extensionId The unique identifier of the extension that generated the event. + * + * @return A new \c CSEvent object + */ +function CSEvent(type, scope, appId, extensionId) +{ + this.type = type; + this.scope = scope; + this.appId = appId; + this.extensionId = extensionId; +} + +/** Event-specific data. */ +CSEvent.prototype.data = ""; + +/** + * @class SystemPath + * Stores operating-system-specific location constants for use in the + * \c #CSInterface.getSystemPath() method. + * @return A new \c SystemPath object. + */ +function SystemPath() +{ +} + +/** The path to user data. */ +SystemPath.USER_DATA = "userData"; + +/** The path to common files for Adobe applications. */ +SystemPath.COMMON_FILES = "commonFiles"; + +/** The path to the user's default document folder. */ +SystemPath.MY_DOCUMENTS = "myDocuments"; + +/** @deprecated. Use \c #SystemPath.Extension. */ +SystemPath.APPLICATION = "application"; + +/** The path to current extension. */ +SystemPath.EXTENSION = "extension"; + +/** The path to hosting application's executable. */ +SystemPath.HOST_APPLICATION = "hostApplication"; + +/** + * @class ColorType + * Stores color-type constants. + */ +function ColorType() +{ +} + +/** RGB color type. */ +ColorType.RGB = "rgb"; + +/** Gradient color type. */ +ColorType.GRADIENT = "gradient"; + +/** Null color type. */ +ColorType.NONE = "none"; + +/** + * @class RGBColor + * Stores an RGB color with red, green, blue, and alpha values. + * All values are in the range [0.0 to 255.0]. Invalid numeric values are + * converted to numbers within this range. + * + * @param red The red value, in the range [0.0 to 255.0]. + * @param green The green value, in the range [0.0 to 255.0]. + * @param blue The blue value, in the range [0.0 to 255.0]. + * @param alpha The alpha (transparency) value, in the range [0.0 to 255.0]. + * The default, 255.0, means that the color is fully opaque. + * + * @return A new RGBColor object. + */ +function RGBColor(red, green, blue, alpha) +{ + this.red = red; + this.green = green; + this.blue = blue; + this.alpha = alpha; +} + +/** + * @class Direction + * A point value in which the y component is 0 and the x component + * is positive or negative for a right or left direction, + * or the x component is 0 and the y component is positive or negative for + * an up or down direction. + * + * @param x The horizontal component of the point. + * @param y The vertical component of the point. + * + * @return A new \c Direction object. + */ +function Direction(x, y) +{ + this.x = x; + this.y = y; +} + +/** + * @class GradientStop + * Stores gradient stop information. + * + * @param offset The offset of the gradient stop, in the range [0.0 to 1.0]. + * @param rgbColor The color of the gradient at this point, an \c #RGBColor object. + * + * @return GradientStop object. + */ +function GradientStop(offset, rgbColor) +{ + this.offset = offset; + this.rgbColor = rgbColor; +} + +/** + * @class GradientColor + * Stores gradient color information. + * + * @param type The gradient type, must be "linear". + * @param direction A \c #Direction object for the direction of the gradient + (up, down, right, or left). + * @param numStops The number of stops in the gradient. + * @param gradientStopList An array of \c #GradientStop objects. + * + * @return A new \c GradientColor object. + */ +function GradientColor(type, direction, numStops, arrGradientStop) +{ + this.type = type; + this.direction = direction; + this.numStops = numStops; + this.arrGradientStop = arrGradientStop; +} + +/** + * @class UIColor + * Stores color information, including the type, anti-alias level, and specific color + * values in a color object of an appropriate type. + * + * @param type The color type, 1 for "rgb" and 2 for "gradient". + The supplied color object must correspond to this type. + * @param antialiasLevel The anti-alias level constant. + * @param color A \c #RGBColor or \c #GradientColor object containing specific color information. + * + * @return A new \c UIColor object. + */ +function UIColor(type, antialiasLevel, color) +{ + this.type = type; + this.antialiasLevel = antialiasLevel; + this.color = color; +} + +/** + * @class AppSkinInfo + * Stores window-skin properties, such as color and font. All color parameter values are \c #UIColor objects except that systemHighlightColor is \c #RGBColor object. + * + * @param baseFontFamily The base font family of the application. + * @param baseFontSize The base font size of the application. + * @param appBarBackgroundColor The application bar background color. + * @param panelBackgroundColor The background color of the extension panel. + * @param appBarBackgroundColorSRGB The application bar background color, as sRGB. + * @param panelBackgroundColorSRGB The background color of the extension panel, as sRGB. + * @param systemHighlightColor The highlight color of the extension panel, if provided by the host application. Otherwise, the operating-system highlight color. + * + * @return AppSkinInfo object. + */ +function AppSkinInfo(baseFontFamily, baseFontSize, appBarBackgroundColor, panelBackgroundColor, appBarBackgroundColorSRGB, panelBackgroundColorSRGB, systemHighlightColor) +{ + this.baseFontFamily = baseFontFamily; + this.baseFontSize = baseFontSize; + this.appBarBackgroundColor = appBarBackgroundColor; + this.panelBackgroundColor = panelBackgroundColor; + this.appBarBackgroundColorSRGB = appBarBackgroundColorSRGB; + this.panelBackgroundColorSRGB = panelBackgroundColorSRGB; + this.systemHighlightColor = systemHighlightColor; +} + +/** + * @class HostEnvironment + * Stores information about the environment in which the extension is loaded. + * + * @param appName The application's name. + * @param appVersion The application's version. + * @param appLocale The application's current license locale. + * @param appUILocale The application's current UI locale. + * @param appId The application's unique identifier. + * @param isAppOnline True if the application is currently online. + * @param appSkinInfo An \c #AppSkinInfo object containing the application's default color and font styles. + * + * @return A new \c HostEnvironment object. + */ +function HostEnvironment(appName, appVersion, appLocale, appUILocale, appId, isAppOnline, appSkinInfo) +{ + this.appName = appName; + this.appVersion = appVersion; + this.appLocale = appLocale; + this.appUILocale = appUILocale; + this.appId = appId; + this.isAppOnline = isAppOnline; + this.appSkinInfo = appSkinInfo; +} + +/** + * @class HostCapabilities + * Stores information about the host capabilities. + * + * @param EXTENDED_PANEL_MENU True if the application supports panel menu. + * @param EXTENDED_PANEL_ICONS True if the application supports panel icon. + * @param DELEGATE_APE_ENGINE True if the application supports delegated APE engine. + * @param SUPPORT_HTML_EXTENSIONS True if the application supports HTML extensions. + * @param DISABLE_FLASH_EXTENSIONS True if the application disables FLASH extensions. + * + * @return A new \c HostCapabilities object. + */ +function HostCapabilities(EXTENDED_PANEL_MENU, EXTENDED_PANEL_ICONS, DELEGATE_APE_ENGINE, SUPPORT_HTML_EXTENSIONS, DISABLE_FLASH_EXTENSIONS) +{ + this.EXTENDED_PANEL_MENU = EXTENDED_PANEL_MENU; + this.EXTENDED_PANEL_ICONS = EXTENDED_PANEL_ICONS; + this.DELEGATE_APE_ENGINE = DELEGATE_APE_ENGINE; + this.SUPPORT_HTML_EXTENSIONS = SUPPORT_HTML_EXTENSIONS; + this.DISABLE_FLASH_EXTENSIONS = DISABLE_FLASH_EXTENSIONS; // Since 5.0.0 +} + +/** + * @class ApiVersion + * Stores current api version. + * + * Since 4.2.0 + * + * @param major The major version + * @param minor The minor version. + * @param micro The micro version. + * + * @return ApiVersion object. + */ +function ApiVersion(major, minor, micro) +{ + this.major = major; + this.minor = minor; + this.micro = micro; +} + +/** + * @class MenuItemStatus + * Stores flyout menu item status + * + * Since 5.2.0 + * + * @param menuItemLabel The menu item label. + * @param enabled True if user wants to enable the menu item. + * @param checked True if user wants to check the menu item. + * + * @return MenuItemStatus object. + */ +function MenuItemStatus(menuItemLabel, enabled, checked) +{ + this.menuItemLabel = menuItemLabel; + this.enabled = enabled; + this.checked = checked; +} + +/** + * @class ContextMenuItemStatus + * Stores the status of the context menu item. + * + * Since 5.2.0 + * + * @param menuItemID The menu item id. + * @param enabled True if user wants to enable the menu item. + * @param checked True if user wants to check the menu item. + * + * @return MenuItemStatus object. + */ +function ContextMenuItemStatus(menuItemID, enabled, checked) +{ + this.menuItemID = menuItemID; + this.enabled = enabled; + this.checked = checked; +} +//------------------------------ CSInterface ---------------------------------- + +/** + * @class CSInterface + * This is the entry point to the CEP extensibility infrastructure. + * Instantiate this object and use it to: + *
    + *
  • Access information about the host application in which an extension is running
  • + *
  • Launch an extension
  • + *
  • Register interest in event notifications, and dispatch events
  • + *
+ * + * @return A new \c CSInterface object + */ +function CSInterface() +{ +} + +/** + * User can add this event listener to handle native application theme color changes. + * Callback function gives extensions ability to fine-tune their theme color after the + * global theme color has been changed. + * The callback function should be like below: + * + * @example + * // event is a CSEvent object, but user can ignore it. + * function OnAppThemeColorChanged(event) + * { + * // Should get a latest HostEnvironment object from application. + * var skinInfo = JSON.parse(window.__adobe_cep__.getHostEnvironment()).appSkinInfo; + * // Gets the style information such as color info from the skinInfo, + * // and redraw all UI controls of your extension according to the style info. + * } + */ +CSInterface.THEME_COLOR_CHANGED_EVENT = "com.adobe.csxs.events.ThemeColorChanged"; + +/** The host environment data object. */ +CSInterface.prototype.hostEnvironment = window.__adobe_cep__ ? JSON.parse(window.__adobe_cep__.getHostEnvironment()) : null; + +/** Retrieves information about the host environment in which the + * extension is currently running. + * + * @return A \c #HostEnvironment object. + */ +CSInterface.prototype.getHostEnvironment = function() +{ + this.hostEnvironment = JSON.parse(window.__adobe_cep__.getHostEnvironment()); + return this.hostEnvironment; +}; + +/** Closes this extension. */ +CSInterface.prototype.closeExtension = function() +{ + window.__adobe_cep__.closeExtension(); +}; + +/** + * Retrieves a path for which a constant is defined in the system. + * + * @param pathType The path-type constant defined in \c #SystemPath , + * + * @return The platform-specific system path string. + */ +CSInterface.prototype.getSystemPath = function(pathType) +{ + var path = decodeURI(window.__adobe_cep__.getSystemPath(pathType)); + var OSVersion = this.getOSInformation(); + if (OSVersion.indexOf("Windows") >= 0) + { + path = path.replace("file:///", ""); + } + else if (OSVersion.indexOf("Mac") >= 0) + { + path = path.replace("file://", ""); + } + return path; +}; + +/** + * Evaluates a JavaScript script, which can use the JavaScript DOM + * of the host application. + * + * @param script The JavaScript script. + * @param callback Optional. A callback function that receives the result of execution. + * If execution fails, the callback function receives the error message \c EvalScript_ErrMessage. + */ +CSInterface.prototype.evalScript = function(script, callback) +{ + if(callback === null || callback === undefined) + { + callback = function(result){}; + } + window.__adobe_cep__.evalScript(script, callback); +}; + +/** + * Retrieves the unique identifier of the application. + * in which the extension is currently running. + * + * @return The unique ID string. + */ +CSInterface.prototype.getApplicationID = function() +{ + var appId = this.hostEnvironment.appId; + return appId; +}; + +/** + * Retrieves host capability information for the application + * in which the extension is currently running. + * + * @return A \c #HostCapabilities object. + */ +CSInterface.prototype.getHostCapabilities = function() +{ + var hostCapabilities = JSON.parse(window.__adobe_cep__.getHostCapabilities() ); + return hostCapabilities; +}; + +/** + * Triggers a CEP event programmatically. Yoy can use it to dispatch + * an event of a predefined type, or of a type you have defined. + * + * @param event A \c CSEvent object. + */ +CSInterface.prototype.dispatchEvent = function(event) +{ + if (typeof event.data == "object") + { + event.data = JSON.stringify(event.data); + } + + window.__adobe_cep__.dispatchEvent(event); +}; + +/** + * Registers an interest in a CEP event of a particular type, and + * assigns an event handler. + * The event infrastructure notifies your extension when events of this type occur, + * passing the event object to the registered handler function. + * + * @param type The name of the event type of interest. + * @param listener The JavaScript handler function or method. + * @param obj Optional, the object containing the handler method, if any. + * Default is null. + */ +CSInterface.prototype.addEventListener = function(type, listener, obj) +{ + window.__adobe_cep__.addEventListener(type, listener, obj); +}; + +/** + * Removes a registered event listener. + * + * @param type The name of the event type of interest. + * @param listener The JavaScript handler function or method that was registered. + * @param obj Optional, the object containing the handler method, if any. + * Default is null. + */ +CSInterface.prototype.removeEventListener = function(type, listener, obj) +{ + window.__adobe_cep__.removeEventListener(type, listener, obj); +}; + +/** + * Loads and launches another extension, or activates the extension if it is already loaded. + * + * @param extensionId The extension's unique identifier. + * @param startupParams Not currently used, pass "". + * + * @example + * To launch the extension "help" with ID "HLP" from this extension, call: + * requestOpenExtension("HLP", ""); + * + */ +CSInterface.prototype.requestOpenExtension = function(extensionId, params) +{ + window.__adobe_cep__.requestOpenExtension(extensionId, params); +}; + +/** + * Retrieves the list of extensions currently loaded in the current host application. + * The extension list is initialized once, and remains the same during the lifetime + * of the CEP session. + * + * @param extensionIds Optional, an array of unique identifiers for extensions of interest. + * If omitted, retrieves data for all extensions. + * + * @return Zero or more \c #Extension objects. + */ +CSInterface.prototype.getExtensions = function(extensionIds) +{ + var extensionIdsStr = JSON.stringify(extensionIds); + var extensionsStr = window.__adobe_cep__.getExtensions(extensionIdsStr); + + var extensions = JSON.parse(extensionsStr); + return extensions; +}; + +/** + * Retrieves network-related preferences. + * + * @return A JavaScript object containing network preferences. + */ +CSInterface.prototype.getNetworkPreferences = function() +{ + var result = window.__adobe_cep__.getNetworkPreferences(); + var networkPre = JSON.parse(result); + + return networkPre; +}; + +/** + * Initializes the resource bundle for this extension with property values + * for the current application and locale. + * To support multiple locales, you must define a property file for each locale, + * containing keyed display-string values for that locale. + * See localization documentation for Extension Builder and related products. + * + * Keys can be in the + * form key.value="localized string", for use in HTML text elements. + * For example, in this input element, the localized \c key.value string is displayed + * instead of the empty \c value string: + * + * + * + * @return An object containing the resource bundle information. + */ +CSInterface.prototype.initResourceBundle = function() +{ + var resourceBundle = JSON.parse(window.__adobe_cep__.initResourceBundle()); + var resElms = document.querySelectorAll('[data-locale]'); + for (var n = 0; n < resElms.length; n++) + { + var resEl = resElms[n]; + // Get the resource key from the element. + var resKey = resEl.getAttribute('data-locale'); + if (resKey) + { + // Get all the resources that start with the key. + for (var key in resourceBundle) + { + if (key.indexOf(resKey) === 0) + { + var resValue = resourceBundle[key]; + if (key.length == resKey.length) + { + resEl.innerHTML = resValue; + } + else if ('.' == key.charAt(resKey.length)) + { + var attrKey = key.substring(resKey.length + 1); + resEl[attrKey] = resValue; + } + } + } + } + } + return resourceBundle; +}; + +/** + * Writes installation information to a file. + * + * @return The file path. + */ +CSInterface.prototype.dumpInstallationInfo = function() +{ + return window.__adobe_cep__.dumpInstallationInfo(); +}; + +/** + * Retrieves version information for the current Operating System, + * See http://www.useragentstring.com/pages/Chrome/ for Chrome \c navigator.userAgent values. + * + * @return A string containing the OS version, or "unknown Operation System". + * If user customizes the User Agent by setting CEF command parameter "--user-agent", only + * "Mac OS X" or "Windows" will be returned. + */ +CSInterface.prototype.getOSInformation = function() +{ + var userAgent = navigator.userAgent; + + if ((navigator.platform == "Win32") || (navigator.platform == "Windows")) + { + var winVersion = "Windows"; + var winBit = ""; + if (userAgent.indexOf("Windows") > -1) + { + if (userAgent.indexOf("Windows NT 5.0") > -1) + { + winVersion = "Windows 2000"; + } + else if (userAgent.indexOf("Windows NT 5.1") > -1) + { + winVersion = "Windows XP"; + } + else if (userAgent.indexOf("Windows NT 5.2") > -1) + { + winVersion = "Windows Server 2003"; + } + else if (userAgent.indexOf("Windows NT 6.0") > -1) + { + winVersion = "Windows Vista"; + } + else if (userAgent.indexOf("Windows NT 6.1") > -1) + { + winVersion = "Windows 7"; + } + else if (userAgent.indexOf("Windows NT 6.2") > -1) + { + winVersion = "Windows 8"; + } + else if (userAgent.indexOf("Windows NT 6.3") > -1) + { + winVersion = "Windows 8.1"; + } + else if (userAgent.indexOf("Windows NT 10") > -1) + { + winVersion = "Windows 10"; + } + + if (userAgent.indexOf("WOW64") > -1 || userAgent.indexOf("Win64") > -1) + { + winBit = " 64-bit"; + } + else + { + winBit = " 32-bit"; + } + } + + return winVersion + winBit; + } + else if ((navigator.platform == "MacIntel") || (navigator.platform == "Macintosh")) + { + var result = "Mac OS X"; + + if (userAgent.indexOf("Mac OS X") > -1) + { + result = userAgent.substring(userAgent.indexOf("Mac OS X"), userAgent.indexOf(")")); + result = result.replace(/_/g, "."); + } + + return result; + } + + return "Unknown Operation System"; +}; + +/** + * Opens a page in the default system browser. + * + * Since 4.2.0 + * + * @param url The URL of the page/file to open, or the email address. + * Must use HTTP/HTTPS/file/mailto protocol. For example: + * "http://www.adobe.com" + * "https://github.com" + * "file:///C:/log.txt" + * "mailto:test@adobe.com" + * + * @return One of these error codes:\n + *
    \n + *
  • NO_ERROR - 0
  • \n + *
  • ERR_UNKNOWN - 1
  • \n + *
  • ERR_INVALID_PARAMS - 2
  • \n + *
  • ERR_INVALID_URL - 201
  • \n + *
\n + */ +CSInterface.prototype.openURLInDefaultBrowser = function(url) +{ + return cep.util.openURLInDefaultBrowser(url); +}; + +/** + * Retrieves extension ID. + * + * Since 4.2.0 + * + * @return extension ID. + */ +CSInterface.prototype.getExtensionID = function() +{ + return window.__adobe_cep__.getExtensionId(); +}; + +/** + * Retrieves the scale factor of screen. + * On Windows platform, the value of scale factor might be different from operating system's scale factor, + * since host application may use its self-defined scale factor. + * + * Since 4.2.0 + * + * @return One of the following float number. + *
    \n + *
  • -1.0 when error occurs
  • \n + *
  • 1.0 means normal screen
  • \n + *
  • >1.0 means HiDPI screen
  • \n + *
\n + */ +CSInterface.prototype.getScaleFactor = function() +{ + return window.__adobe_cep__.getScaleFactor(); +}; + +/** + * Set a handler to detect any changes of scale factor. This only works on Mac. + * + * Since 4.2.0 + * + * @param handler The function to be called when scale factor is changed. + * + */ +CSInterface.prototype.setScaleFactorChangedHandler = function(handler) +{ + window.__adobe_cep__.setScaleFactorChangedHandler(handler); +}; + +/** + * Retrieves current API version. + * + * Since 4.2.0 + * + * @return ApiVersion object. + * + */ +CSInterface.prototype.getCurrentApiVersion = function() +{ + var apiVersion = JSON.parse(window.__adobe_cep__.getCurrentApiVersion()); + return apiVersion; +}; + +/** + * Set panel flyout menu by an XML. + * + * Since 5.2.0 + * + * Register a callback function for "com.adobe.csxs.events.flyoutMenuClicked" to get notified when a + * menu item is clicked. + * The "data" attribute of event is an object which contains "menuId" and "menuName" attributes. + * + * Register callback functions for "com.adobe.csxs.events.flyoutMenuOpened" and "com.adobe.csxs.events.flyoutMenuClosed" + * respectively to get notified when flyout menu is opened or closed. + * + * @param menu A XML string which describes menu structure. + * An example menu XML: + * + * + * + * + * + * + * + * + * + * + * + * + */ +CSInterface.prototype.setPanelFlyoutMenu = function(menu) +{ + if ("string" != typeof menu) + { + return; + } + + window.__adobe_cep__.invokeSync("setPanelFlyoutMenu", menu); +}; + +/** + * Updates a menu item in the extension window's flyout menu, by setting the enabled + * and selection status. + * + * Since 5.2.0 + * + * @param menuItemLabel The menu item label. + * @param enabled True to enable the item, false to disable it (gray it out). + * @param checked True to select the item, false to deselect it. + * + * @return false when the host application does not support this functionality (HostCapabilities.EXTENDED_PANEL_MENU is false). + * Fails silently if menu label is invalid. + * + * @see HostCapabilities.EXTENDED_PANEL_MENU + */ +CSInterface.prototype.updatePanelMenuItem = function(menuItemLabel, enabled, checked) +{ + var ret = false; + if (this.getHostCapabilities().EXTENDED_PANEL_MENU) + { + var itemStatus = new MenuItemStatus(menuItemLabel, enabled, checked); + ret = window.__adobe_cep__.invokeSync("updatePanelMenuItem", JSON.stringify(itemStatus)); + } + return ret; +}; + + +/** + * Set context menu by XML string. + * + * Since 5.2.0 + * + * There are a number of conventions used to communicate what type of menu item to create and how it should be handled. + * - an item without menu ID or menu name is disabled and is not shown. + * - if the item name is "---" (three hyphens) then it is treated as a separator. The menu ID in this case will always be NULL. + * - Checkable attribute takes precedence over Checked attribute. + * - a PNG icon. For optimal display results please supply a 16 x 16px icon as larger dimensions will increase the size of the menu item. + The Chrome extension contextMenus API was taken as a reference. + https://developer.chrome.com/extensions/contextMenus + * - the items with icons and checkable items cannot coexist on the same menu level. The former take precedences over the latter. + * + * @param menu A XML string which describes menu structure. + * @param callback The callback function which is called when a menu item is clicked. The only parameter is the returned ID of clicked menu item. + * + * @description An example menu XML: + * + * + * + * + * + * + * + * + * + * + * + */ +CSInterface.prototype.setContextMenu = function(menu, callback) +{ + if ("string" != typeof menu) + { + return; + } + + window.__adobe_cep__.invokeAsync("setContextMenu", menu, callback); +}; + +/** + * Set context menu by JSON string. + * + * Since 6.0.0 + * + * There are a number of conventions used to communicate what type of menu item to create and how it should be handled. + * - an item without menu ID or menu name is disabled and is not shown. + * - if the item label is "---" (three hyphens) then it is treated as a separator. The menu ID in this case will always be NULL. + * - Checkable attribute takes precedence over Checked attribute. + * - a PNG icon. For optimal display results please supply a 16 x 16px icon as larger dimensions will increase the size of the menu item. + The Chrome extension contextMenus API was taken as a reference. + * - the items with icons and checkable items cannot coexist on the same menu level. The former take precedences over the latter. + https://developer.chrome.com/extensions/contextMenus + * + * @param menu A JSON string which describes menu structure. + * @param callback The callback function which is called when a menu item is clicked. The only parameter is the returned ID of clicked menu item. + * + * @description An example menu JSON: + * + * { + * "menu": [ + * { + * "id": "menuItemId1", + * "label": "testExample1", + * "enabled": true, + * "checkable": true, + * "checked": false, + * "icon": "./image/small_16X16.png" + * }, + * { + * "id": "menuItemId2", + * "label": "testExample2", + * "menu": [ + * { + * "id": "menuItemId2-1", + * "label": "testExample2-1", + * "menu": [ + * { + * "id": "menuItemId2-1-1", + * "label": "testExample2-1-1", + * "enabled": false, + * "checkable": true, + * "checked": true + * } + * ] + * }, + * { + * "id": "menuItemId2-2", + * "label": "testExample2-2", + * "enabled": true, + * "checkable": true, + * "checked": true + * } + * ] + * }, + * { + * "label": "---" + * }, + * { + * "id": "menuItemId3", + * "label": "testExample3", + * "enabled": false, + * "checkable": true, + * "checked": false + * } + * ] + * } + * + */ +CSInterface.prototype.setContextMenuByJSON = function(menu, callback) +{ + if ("string" != typeof menu) + { + return; + } + + window.__adobe_cep__.invokeAsync("setContextMenuByJSON", menu, callback); +}; + +/** + * Updates a context menu item by setting the enabled and selection status. + * + * Since 5.2.0 + * + * @param menuItemID The menu item ID. + * @param enabled True to enable the item, false to disable it (gray it out). + * @param checked True to select the item, false to deselect it. + */ +CSInterface.prototype.updateContextMenuItem = function(menuItemID, enabled, checked) +{ + var itemStatus = new ContextMenuItemStatus(menuItemID, enabled, checked); + ret = window.__adobe_cep__.invokeSync("updateContextMenuItem", JSON.stringify(itemStatus)); +}; + +/** + * Get the visibility status of an extension window. + * + * Since 6.0.0 + * + * @return true if the extension window is visible; false if the extension window is hidden. + */ +CSInterface.prototype.isWindowVisible = function() +{ + return window.__adobe_cep__.invokeSync("isWindowVisible", ""); +}; + +/** + * Resize extension's content to the specified dimensions. + * 1. Works with modal and modeless extensions in all Adobe products. + * 2. Extension's manifest min/max size constraints apply and take precedence. + * 3. For panel extensions + * 3.1 This works in all Adobe products except: + * * Premiere Pro + * * Prelude + * * After Effects + * 3.2 When the panel is in certain states (especially when being docked), + * it will not change to the desired dimensions even when the + * specified size satisfies min/max constraints. + * + * Since 6.0.0 + * + * @param width The new width + * @param height The new height + */ +CSInterface.prototype.resizeContent = function(width, height) +{ + window.__adobe_cep__.resizeContent(width, height); +}; + +/** + * Register the invalid certificate callback for an extension. + * This callback will be triggered when the extension tries to access the web site that contains the invalid certificate on the main frame. + * But if the extension does not call this function and tries to access the web site containing the invalid certificate, a default error page will be shown. + * + * Since 6.1.0 + * + * @param callback the callback function + */ +CSInterface.prototype.registerInvalidCertificateCallback = function(callback) +{ + return window.__adobe_cep__.registerInvalidCertificateCallback(callback); +}; + +/** + * Register an interest in some key events to prevent them from being sent to the host application. + * + * This function works with modeless extensions and panel extensions. + * Generally all the key events will be sent to the host application for these two extensions if the current focused element + * is not text input or dropdown, + * If you want to intercept some key events and want them to be handled in the extension, please call this function + * in advance to prevent them being sent to the host application. + * + * Since 6.1.0 + * + * @param keyEventsInterest A JSON string describing those key events you are interested in. A null object or + an empty string will lead to removing the interest + * + * This JSON string should be an array, each object has following keys: + * + * keyCode: [Required] represents an OS system dependent virtual key code identifying + * the unmodified value of the pressed key. + * ctrlKey: [optional] a Boolean that indicates if the control key was pressed (true) or not (false) when the event occurred. + * altKey: [optional] a Boolean that indicates if the alt key was pressed (true) or not (false) when the event occurred. + * shiftKey: [optional] a Boolean that indicates if the shift key was pressed (true) or not (false) when the event occurred. + * metaKey: [optional] (Mac Only) a Boolean that indicates if the Meta key was pressed (true) or not (false) when the event occurred. + * On Macintosh keyboards, this is the command key. To detect Windows key on Windows, please use keyCode instead. + * An example JSON string: + * + * [ + * { + * "keyCode": 48 + * }, + * { + * "keyCode": 123, + * "ctrlKey": true + * }, + * { + * "keyCode": 123, + * "ctrlKey": true, + * "metaKey": true + * } + * ] + * + */ +CSInterface.prototype.registerKeyEventsInterest = function(keyEventsInterest) +{ + return window.__adobe_cep__.registerKeyEventsInterest(keyEventsInterest); +}; + +/** + * Set the title of the extension window. + * This function works with modal and modeless extensions in all Adobe products, and panel extensions in Photoshop, InDesign, InCopy, Illustrator, Flash Pro and Dreamweaver. + * + * Since 6.1.0 + * + * @param title The window title. + */ +CSInterface.prototype.setWindowTitle = function(title) +{ + window.__adobe_cep__.invokeSync("setWindowTitle", title); +}; + +/** + * Get the title of the extension window. + * This function works with modal and modeless extensions in all Adobe products, and panel extensions in Photoshop, InDesign, InCopy, Illustrator, Flash Pro and Dreamweaver. + * + * Since 6.1.0 + * + * @return The window title. + */ +CSInterface.prototype.getWindowTitle = function() +{ + return window.__adobe_cep__.invokeSync("getWindowTitle", ""); +}; diff --git a/openpype/hosts/photoshop/api/extension/client/client.js b/openpype/hosts/photoshop/api/extension/client/client.js new file mode 100644 index 00000000000..f4ba4cfe47b --- /dev/null +++ b/openpype/hosts/photoshop/api/extension/client/client.js @@ -0,0 +1,300 @@ + // client facing part of extension, creates WSRPC client (jsx cannot + // do that) + // consumes RPC calls from server (OpenPype) calls ./host/index.jsx and + // returns values back (in json format) + + var logReturn = function(result){ log.warn('Result: ' + result);}; + + var csInterface = new CSInterface(); + + log.warn("script start"); + + WSRPC.DEBUG = false; + WSRPC.TRACE = false; + + function myCallBack(){ + log.warn("Triggered index.jsx"); + } + // importing through manifest.xml isn't working because relative paths + // possibly TODO + jsx.evalFile('./host/index.jsx', myCallBack); + + function runEvalScript(script) { + // because of asynchronous nature of functions in jsx + // this waits for response + return new Promise(function(resolve, reject){ + csInterface.evalScript(script, resolve); + }); + } + + /** main entry point **/ + startUp("WEBSOCKET_URL"); + + // get websocket server url from environment value + async function startUp(url){ + log.warn("url", url); + promis = runEvalScript("getEnv('" + url + "')"); + + var res = await promis; + // run rest only after resolved promise + main(res); + } + + function get_extension_version(){ + /** Returns version number from extension manifest.xml **/ + log.debug("get_extension_version") + var path = csInterface.getSystemPath(SystemPath.EXTENSION); + log.debug("extension path " + path); + + var result = window.cep.fs.readFile(path + "/CSXS/manifest.xml"); + var version = undefined; + if(result.err === 0){ + if (window.DOMParser) { + const parser = new DOMParser(); + const xmlDoc = parser.parseFromString(result.data.toString(), 'text/xml'); + const children = xmlDoc.children; + + for (let i = 0; i <= children.length; i++) { + if (children[i] && children[i].getAttribute('ExtensionBundleVersion')) { + version = children[i].getAttribute('ExtensionBundleVersion'); + } + } + } + } + return version + } + + function main(websocket_url){ + // creates connection to 'websocket_url', registers routes + log.warn("websocket_url", websocket_url); + var default_url = 'ws://localhost:8099/ws/'; + + if (websocket_url == ''){ + websocket_url = default_url; + } + log.warn("connecting to:", websocket_url); + RPC = new WSRPC(websocket_url, 5000); // spin connection + + RPC.connect(); + + log.warn("connected"); + + function EscapeStringForJSX(str){ + // Replaces: + // \ with \\ + // ' with \' + // " with \" + // See: https://stackoverflow.com/a/3967927/5285364 + return str.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/"/g, '\\"'); + } + + RPC.addRoute('Photoshop.open', function (data) { + log.warn('Server called client route "open":', data); + var escapedPath = EscapeStringForJSX(data.path); + return runEvalScript("fileOpen('" + escapedPath +"')") + .then(function(result){ + log.warn("open: " + result); + return result; + }); + }); + + RPC.addRoute('Photoshop.read', function (data) { + log.warn('Server called client route "read":', data); + return runEvalScript("getHeadline()") + .then(function(result){ + log.warn("getHeadline: " + result); + return result; + }); + }); + + RPC.addRoute('Photoshop.get_layers', function (data) { + log.warn('Server called client route "get_layers":', data); + return runEvalScript("getLayers()") + .then(function(result){ + log.warn("getLayers: " + result); + return result; + }); + }); + + RPC.addRoute('Photoshop.set_visible', function (data) { + log.warn('Server called client route "set_visible":', data); + return runEvalScript("setVisible(" + data.layer_id + ", " + + data.visibility + ")") + .then(function(result){ + log.warn("setVisible: " + result); + return result; + }); + }); + + RPC.addRoute('Photoshop.get_active_document_name', function (data) { + log.warn('Server called client route "get_active_document_name":', + data); + return runEvalScript("getActiveDocumentName()") + .then(function(result){ + log.warn("save: " + result); + return result; + }); + }); + + RPC.addRoute('Photoshop.get_active_document_full_name', function (data) { + log.warn('Server called client route ' + + '"get_active_document_full_name":', data); + return runEvalScript("getActiveDocumentFullName()") + .then(function(result){ + log.warn("save: " + result); + return result; + }); + }); + + RPC.addRoute('Photoshop.save', function (data) { + log.warn('Server called client route "save":', data); + + return runEvalScript("save()") + .then(function(result){ + log.warn("save: " + result); + return result; + }); + }); + + RPC.addRoute('Photoshop.get_selected_layers', function (data) { + log.warn('Server called client route "get_selected_layers":', data); + + return runEvalScript("getSelectedLayers()") + .then(function(result){ + log.warn("get_selected_layers: " + result); + return result; + }); + }); + + RPC.addRoute('Photoshop.create_group', function (data) { + log.warn('Server called client route "create_group":', data); + + return runEvalScript("createGroup('" + data.name + "')") + .then(function(result){ + log.warn("createGroup: " + result); + return result; + }); + }); + + RPC.addRoute('Photoshop.group_selected_layers', function (data) { + log.warn('Server called client route "group_selected_layers":', + data); + + return runEvalScript("groupSelectedLayers(null, "+ + "'" + data.name +"')") + .then(function(result){ + log.warn("group_selected_layers: " + result); + return result; + }); + }); + + RPC.addRoute('Photoshop.import_smart_object', function (data) { + log.warn('Server called client "import_smart_object":', data); + var escapedPath = EscapeStringForJSX(data.path); + return runEvalScript("importSmartObject('" + escapedPath +"', " + + "'"+ data.name +"',"+ + + data.as_reference +")") + .then(function(result){ + log.warn("import_smart_object: " + result); + return result; + }); + }); + + RPC.addRoute('Photoshop.replace_smart_object', function (data) { + log.warn('Server called route "replace_smart_object":', data); + var escapedPath = EscapeStringForJSX(data.path); + return runEvalScript("replaceSmartObjects("+data.layer_id+"," + + "'" + escapedPath +"',"+ + "'"+ data.name +"')") + .then(function(result){ + log.warn("replaceSmartObjects: " + result); + return result; + }); + }); + + RPC.addRoute('Photoshop.delete_layer', function (data) { + log.warn('Server called route "delete_layer":', data); + return runEvalScript("deleteLayer("+data.layer_id+")") + .then(function(result){ + log.warn("delete_layer: " + result); + return result; + }); + }); + + RPC.addRoute('Photoshop.rename_layer', function (data) { + log.warn('Server called route "rename_layer":', data); + return runEvalScript("renameLayer("+data.layer_id+", " + + "'"+ data.name +"')") + .then(function(result){ + log.warn("rename_layer: " + result); + return result; + }); +}); + + RPC.addRoute('Photoshop.select_layers', function (data) { + log.warn('Server called client route "select_layers":', data); + + return runEvalScript("selectLayers('" + data.layers +"')") + .then(function(result){ + log.warn("select_layers: " + result); + return result; + }); + }); + + RPC.addRoute('Photoshop.is_saved', function (data) { + log.warn('Server called client route "is_saved":', data); + + return runEvalScript("isSaved()") + .then(function(result){ + log.warn("is_saved: " + result); + return result; + }); + }); + + RPC.addRoute('Photoshop.saveAs', function (data) { + log.warn('Server called client route "saveAsJPEG":', data); + var escapedPath = EscapeStringForJSX(data.image_path); + return runEvalScript("saveAs('" + escapedPath + "', " + + "'" + data.ext + "', " + + data.as_copy + ")") + .then(function(result){ + log.warn("save: " + result); + return result; + }); + }); + + RPC.addRoute('Photoshop.imprint', function (data) { + log.warn('Server called client route "imprint":', data); + var escaped = data.payload.replace(/\n/g, "\\n"); + return runEvalScript("imprint('" + escaped + "')") + .then(function(result){ + log.warn("imprint: " + result); + return result; + }); + }); + + RPC.addRoute('Photoshop.get_extension_version', function (data) { + log.warn('Server called client route "get_extension_version":', data); + return get_extension_version(); + }); + + RPC.addRoute('Photoshop.close', function (data) { + log.warn('Server called client route "close":', data); + return runEvalScript("close()"); + }); + + RPC.call('Photoshop.ping').then(function (data) { + log.warn('Result for calling server route "ping": ', data); + return runEvalScript("ping()") + .then(function(result){ + log.warn("ping: " + result); + return result; + }); + + }, function (error) { + log.warn(error); + }); + + } + + log.warn("end script"); diff --git a/openpype/hosts/photoshop/api/extension/client/loglevel.min.js b/openpype/hosts/photoshop/api/extension/client/loglevel.min.js new file mode 100644 index 00000000000..648d7e9ff62 --- /dev/null +++ b/openpype/hosts/photoshop/api/extension/client/loglevel.min.js @@ -0,0 +1,2 @@ +/*! loglevel - v1.6.8 - https://github.com/pimterry/loglevel - (c) 2020 Tim Perry - licensed MIT */ +!function(a,b){"use strict";"function"==typeof define&&define.amd?define(b):"object"==typeof module&&module.exports?module.exports=b():a.log=b()}(this,function(){"use strict";function a(a,b){var c=a[b];if("function"==typeof c.bind)return c.bind(a);try{return Function.prototype.bind.call(c,a)}catch(b){return function(){return Function.prototype.apply.apply(c,[a,arguments])}}}function b(){console.log&&(console.log.apply?console.log.apply(console,arguments):Function.prototype.apply.apply(console.log,[console,arguments])),console.trace&&console.trace()}function c(c){return"debug"===c&&(c="log"),typeof console!==i&&("trace"===c&&j?b:void 0!==console[c]?a(console,c):void 0!==console.log?a(console,"log"):h)}function d(a,b){for(var c=0;c=0&&b<=j.levels.SILENT))throw"log.setLevel() called with invalid level: "+b;if(h=b,!1!==c&&e(b),d.call(j,b,a),typeof console===i&&b 1 && arguments[1] !== undefined ? arguments[1] : 1000; + + _classCallCheck(this, WSRPC); + + var self = this; + URL = getAbsoluteWsUrl(URL); + self.id = 1; + self.eventId = 0; + self.socketStarted = false; + self.eventStore = { + onconnect: {}, + onerror: {}, + onclose: {}, + onchange: {} + }; + self.connectionNumber = 0; + self.oneTimeEventStore = { + onconnect: [], + onerror: [], + onclose: [], + onchange: [] + }; + self.callQueue = []; + + function createSocket() { + var ws = new WebSocket(URL); + + var rejectQueue = function rejectQueue() { + self.connectionNumber++; // rejects incoming calls + + var deferred; //reject all pending calls + + while (0 < self.callQueue.length) { + var callObj = self.callQueue.shift(); + deferred = self.store[callObj.id]; + delete self.store[callObj.id]; + + if (deferred && deferred.promise.isPending()) { + deferred.reject('WebSocket error occurred'); + } + } // reject all from the store + + + for (var key in self.store) { + if (!self.store.hasOwnProperty(key)) continue; + deferred = self.store[key]; + + if (deferred && deferred.promise.isPending()) { + deferred.reject('WebSocket error occurred'); + } + } + }; + + function reconnect(callEvents) { + setTimeout(function () { + try { + self.socket = createSocket(); + self.id = 1; + } catch (exc) { + callEvents('onerror', exc); + delete self.socket; + console.error(exc); + } + }, reconnectTimeout); + } + + ws.onclose = function (err) { + log('ONCLOSE CALLED', 'STATE', self.public.state()); + trace(err); + + for (var serial in self.store) { + if (!self.store.hasOwnProperty(serial)) continue; + + if (self.store[serial].hasOwnProperty('reject')) { + self.store[serial].reject('Connection closed'); + } + } + + rejectQueue(); + callEvents('onclose', err); + callEvents('onchange', err); + reconnect(callEvents); + }; + + ws.onerror = function (err) { + log('ONERROR CALLED', 'STATE', self.public.state()); + trace(err); + rejectQueue(); + callEvents('onerror', err); + callEvents('onchange', err); + log('WebSocket has been closed by error: ', err); + }; + + function tryCallEvent(func, event) { + try { + return func(event); + } catch (e) { + if (e.hasOwnProperty('stack')) { + log(e.stack); + } else { + log('Event function', func, 'raised unknown error:', e); + } + + console.error(e); + } + } + + function callEvents(evName, event) { + while (0 < self.oneTimeEventStore[evName].length) { + var deferred = self.oneTimeEventStore[evName].shift(); + if (deferred.hasOwnProperty('resolve') && deferred.promise.isPending()) deferred.resolve(); + } + + for (var i in self.eventStore[evName]) { + if (!self.eventStore[evName].hasOwnProperty(i)) continue; + var cur = self.eventStore[evName][i]; + tryCallEvent(cur, event); + } + } + + ws.onopen = function (ev) { + log('ONOPEN CALLED', 'STATE', self.public.state()); + trace(ev); + + while (0 < self.callQueue.length) { + // noinspection JSUnresolvedFunction + self.socket.send(JSON.stringify(self.callQueue.shift(), 0, 1)); + } + + callEvents('onconnect', ev); + callEvents('onchange', ev); + }; + + function handleCall(self, data) { + if (!self.routes.hasOwnProperty(data.method)) throw new Error('Route not found'); + var connectionNumber = self.connectionNumber; + var deferred = new Deferred(); + deferred.promise.then(function (result) { + if (connectionNumber !== self.connectionNumber) return; + self.socket.send(JSON.stringify({ + id: data.id, + result: result + })); + }, function (error) { + if (connectionNumber !== self.connectionNumber) return; + self.socket.send(JSON.stringify({ + id: data.id, + error: error + })); + }); + var func = self.routes[data.method]; + if (self.asyncRoutes[data.method]) return func.apply(deferred, [data.params]); + + function badPromise() { + throw new Error("You should register route with async flag."); + } + + var promiseMock = { + resolve: badPromise, + reject: badPromise + }; + + try { + deferred.resolve(func.apply(promiseMock, [data.params])); + } catch (e) { + deferred.reject(e); + console.error(e); + } + } + + function handleError(self, data) { + if (!self.store.hasOwnProperty(data.id)) return log('Unknown callback'); + var deferred = self.store[data.id]; + if (typeof deferred === 'undefined') return log('Confirmation without handler'); + delete self.store[data.id]; + log('REJECTING', data.error); + deferred.reject(data.error); + } + + function handleResult(self, data) { + var deferred = self.store[data.id]; + if (typeof deferred === 'undefined') return log('Confirmation without handler'); + delete self.store[data.id]; + + if (data.hasOwnProperty('result')) { + return deferred.resolve(data.result); + } + + return deferred.reject(data.error); + } + + ws.onmessage = function (message) { + log('ONMESSAGE CALLED', 'STATE', self.public.state()); + trace(message); + if (message.type !== 'message') return; + var data; + + try { + data = JSON.parse(message.data); + log(data); + + if (data.hasOwnProperty('method')) { + return handleCall(self, data); + } else if (data.hasOwnProperty('error') && data.error === null) { + return handleError(self, data); + } else { + return handleResult(self, data); + } + } catch (exception) { + var err = { + error: exception.message, + result: null, + id: data ? data.id : null + }; + self.socket.send(JSON.stringify(err)); + console.error(exception); + } + }; + + return ws; + } + + function makeCall(func, args, params) { + self.id += 2; + var deferred = new Deferred(); + var callObj = Object.freeze({ + id: self.id, + method: func, + params: args + }); + var state = self.public.state(); + + if (state === 'OPEN') { + self.store[self.id] = deferred; + self.socket.send(JSON.stringify(callObj)); + } else if (state === 'CONNECTING') { + log('SOCKET IS', state); + self.store[self.id] = deferred; + self.callQueue.push(callObj); + } else { + log('SOCKET IS', state); + + if (params && params['noWait']) { + deferred.reject("Socket is: ".concat(state)); + } else { + self.store[self.id] = deferred; + self.callQueue.push(callObj); + } + } + + return deferred.promise; + } + + self.asyncRoutes = {}; + self.routes = {}; + self.store = {}; + self.public = Object.freeze({ + call: function call(func, args, params) { + return makeCall(func, args, params); + }, + addRoute: function addRoute(route, callback, isAsync) { + self.asyncRoutes[route] = isAsync || false; + self.routes[route] = callback; + }, + deleteRoute: function deleteRoute(route) { + delete self.asyncRoutes[route]; + return delete self.routes[route]; + }, + addEventListener: function addEventListener(event, func) { + var eventId = self.eventId++; + self.eventStore[event][eventId] = func; + return eventId; + }, + removeEventListener: function removeEventListener(event, index) { + if (self.eventStore[event].hasOwnProperty(index)) { + delete self.eventStore[event][index]; + return true; + } else { + return false; + } + }, + onEvent: function onEvent(event) { + var deferred = new Deferred(); + self.oneTimeEventStore[event].push(deferred); + return deferred.promise; + }, + destroy: function destroy() { + return self.socket.close(); + }, + state: function state() { + return readyState[this.stateCode()]; + }, + stateCode: function stateCode() { + if (self.socketStarted && self.socket) return self.socket.readyState; + return 3; + }, + connect: function connect() { + self.socketStarted = true; + self.socket = createSocket(); + } + }); + self.public.addRoute('log', function (argsObj) { + //console.info("Websocket sent: ".concat(argsObj)); + }); + self.public.addRoute('ping', function (data) { + return data; + }); + return self.public; + }; + + WSRPC.DEBUG = false; + WSRPC.TRACE = false; + + return WSRPC; + +})); +//# sourceMappingURL=wsrpc.js.map diff --git a/openpype/hosts/photoshop/api/extension/client/wsrpc.min.js b/openpype/hosts/photoshop/api/extension/client/wsrpc.min.js new file mode 100644 index 00000000000..f1264b91c4e --- /dev/null +++ b/openpype/hosts/photoshop/api/extension/client/wsrpc.min.js @@ -0,0 +1 @@ +!function(global,factory){"object"==typeof exports&&"undefined"!=typeof module?module.exports=factory():"function"==typeof define&&define.amd?define(factory):(global=global||self).WSRPC=factory()}(this,function(){"use strict";function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor))throw new TypeError("Cannot call a class as a function")}function Deferred(){_classCallCheck(this,Deferred);var self=this;function wrapper(func){return function(){if(!self.done)return self.done=!0,func.apply(this,arguments);console.error(new Error("Promise already done"))}}return self.resolve=null,self.reject=null,self.done=!1,self.promise=new Promise(function(resolve,reject){self.resolve=wrapper(resolve),self.reject=wrapper(reject)}),self.promise.isPending=function(){return!self.done},self}function logGroup(group,level,args){console.group(group),console[level].apply(this,args),console.groupEnd()}function log(){WSRPC.DEBUG&&logGroup("WSRPC.DEBUG","trace",arguments)}function trace(msg){if(WSRPC.TRACE){var payload=msg;"data"in msg&&(payload=JSON.parse(msg.data)),logGroup("WSRPC.TRACE","trace",[payload])}}var readyState=Object.freeze({0:"CONNECTING",1:"OPEN",2:"CLOSING",3:"CLOSED"}),WSRPC=function WSRPC(URL){var reconnectTimeout=1 // +// forceEval is now by default true // +// It wraps the scripts in a try catch and an eval providing useful error handling // +// One can set in the jsx engine $.includeStack = true to return the call stack in the event of an error // +/////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// JSX.js for calling jsx code from the js engine // +// 2 methods included // +// 1) jsx.evalScript AKA jsx.eval // +// 2) jsx.evalFile AKA jsx.file // +// Special features // +// 1) Allows all changes in your jsx code to be reloaded into your extension at the click of a button // +// 2) Can enable the $.fileName property to work and provides a $.__fileName() method as an alternative // +// 3) Can force a callBack result from InDesign // +// 4) No more csInterface.evalScript('alert("hello "' + title + " " + name + '");') // +// use jsx.evalScript('alert("hello __title__ __name__");', {title: title, name: name}); // +// 5) execute jsx files from your jsx folder like this jsx.evalFile('myFabJsxScript.jsx'); // +// or from a relative path jsx.evalFile('../myFabScripts/myFabJsxScript.jsx'); // +// or from an absolute url jsx.evalFile('/Path/to/my/FabJsxScript.jsx'); (mac) // +// or from an absolute url jsx.evalFile('C:Path/to/my/FabJsxScript.jsx'); (windows) // +// 6) Parameter can be entered in the from of a parameter list which can be in any order or as an object // +// 7) Not camelCase sensitive (very useful for the illiterate) // +// Dead easy to use BUT SPEND THE 3 TO 5 MINUTES IT SHOULD TAKE TO READ THE INSTRUCTIONS // +/////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/* jshint undef:true, unused:true, esversion:6 */ + +////////////////////////////////////// +// jsx is the interface for the API // +////////////////////////////////////// + +var jsx; + +// Wrap everything in an anonymous function to prevent leeks +(function() { + ///////////////////////////////////////////////////////////////////// + // Substitute some CSInterface functions to avoid dependency on it // + ///////////////////////////////////////////////////////////////////// + + var __dirname = (function() { + var path, isMac; + path = decodeURI(window.__adobe_cep__.getSystemPath('extension')); + isMac = navigator.platform[0] === 'M'; // [M]ac + path = path.replace('file://' + (isMac ? '' : '/'), ''); + return path; + })(); + + var evalScript = function(script, callback) { + callback = callback || function() {}; + window.__adobe_cep__.evalScript(script, callback); + }; + + + //////////////////////////////////////////// + // In place of using the node path module // + //////////////////////////////////////////// + + // jshint undef: true, unused: true + + // A very minified version of the NodeJs Path module!! + // For use outside of NodeJs + // Majorly nicked by Trevor from Joyent + var path = (function() { + + var isString = function(arg) { + return typeof arg === 'string'; + }; + + // var isObject = function(arg) { + // return typeof arg === 'object' && arg !== null; + // }; + + var basename = function(path) { + if (!isString(path)) { + throw new TypeError('Argument to path.basename must be a string'); + } + var bits = path.split(/[\/\\]/g); + return bits[bits.length - 1]; + }; + + // jshint undef: true + // Regex to split a windows path into three parts: [*, device, slash, + // tail] windows-only + var splitDeviceRe = + /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/; + + // Regex to split the tail part of the above into [*, dir, basename, ext] + // var splitTailRe = + // /^([\s\S]*?)((?:\.{1,2}|[^\\\/]+?|)(\.[^.\/\\]*|))(?:[\\\/]*)$/; + + var win32 = {}; + // Function to split a filename into [root, dir, basename, ext] + // var win32SplitPath = function(filename) { + // // Separate device+slash from tail + // var result = splitDeviceRe.exec(filename), + // device = (result[1] || '') + (result[2] || ''), + // tail = result[3] || ''; + // // Split the tail into dir, basename and extension + // var result2 = splitTailRe.exec(tail), + // dir = result2[1], + // basename = result2[2], + // ext = result2[3]; + // return [device, dir, basename, ext]; + // }; + + var win32StatPath = function(path) { + var result = splitDeviceRe.exec(path), + device = result[1] || '', + isUnc = !!device && device[1] !== ':'; + return { + device: device, + isUnc: isUnc, + isAbsolute: isUnc || !!result[2], // UNC paths are always absolute + tail: result[3] + }; + }; + + var normalizeUNCRoot = function(device) { + return '\\\\' + device.replace(/^[\\\/]+/, '').replace(/[\\\/]+/g, '\\'); + }; + + var normalizeArray = function(parts, allowAboveRoot) { + var res = []; + for (var i = 0; i < parts.length; i++) { + var p = parts[i]; + + // ignore empty parts + if (!p || p === '.') + continue; + + if (p === '..') { + if (res.length && res[res.length - 1] !== '..') { + res.pop(); + } else if (allowAboveRoot) { + res.push('..'); + } + } else { + res.push(p); + } + } + + return res; + }; + + win32.normalize = function(path) { + var result = win32StatPath(path), + device = result.device, + isUnc = result.isUnc, + isAbsolute = result.isAbsolute, + tail = result.tail, + trailingSlash = /[\\\/]$/.test(tail); + + // Normalize the tail path + tail = normalizeArray(tail.split(/[\\\/]+/), !isAbsolute).join('\\'); + + if (!tail && !isAbsolute) { + tail = '.'; + } + if (tail && trailingSlash) { + tail += '\\'; + } + + // Convert slashes to backslashes when `device` points to an UNC root. + // Also squash multiple slashes into a single one where appropriate. + if (isUnc) { + device = normalizeUNCRoot(device); + } + + return device + (isAbsolute ? '\\' : '') + tail; + }; + win32.join = function() { + var paths = []; + for (var i = 0; i < arguments.length; i++) { + var arg = arguments[i]; + if (!isString(arg)) { + throw new TypeError('Arguments to path.join must be strings'); + } + if (arg) { + paths.push(arg); + } + } + + var joined = paths.join('\\'); + + // Make sure that the joined path doesn't start with two slashes, because + // normalize() will mistake it for an UNC path then. + // + // This step is skipped when it is very clear that the user actually + // intended to point at an UNC path. This is assumed when the first + // non-empty string arguments starts with exactly two slashes followed by + // at least one more non-slash character. + // + // Note that for normalize() to treat a path as an UNC path it needs to + // have at least 2 components, so we don't filter for that here. + // This means that the user can use join to construct UNC paths from + // a server name and a share name; for example: + // path.join('//server', 'share') -> '\\\\server\\share\') + if (!/^[\\\/]{2}[^\\\/]/.test(paths[0])) { + joined = joined.replace(/^[\\\/]{2,}/, '\\'); + } + return win32.normalize(joined); + }; + + var posix = {}; + + // posix version + posix.join = function() { + var path = ''; + for (var i = 0; i < arguments.length; i++) { + var segment = arguments[i]; + if (!isString(segment)) { + throw new TypeError('Arguments to path.join must be strings'); + } + if (segment) { + if (!path) { + path += segment; + } else { + path += '/' + segment; + } + } + } + return posix.normalize(path); + }; + + // path.normalize(path) + // posix version + posix.normalize = function(path) { + var isAbsolute = path.charAt(0) === '/', + trailingSlash = path && path[path.length - 1] === '/'; + + // Normalize the path + path = normalizeArray(path.split('/'), !isAbsolute).join('/'); + + if (!path && !isAbsolute) { + path = '.'; + } + if (path && trailingSlash) { + path += '/'; + } + + return (isAbsolute ? '/' : '') + path; + }; + + win32.basename = posix.basename = basename; + + this.win32 = win32; + this.posix = posix; + return (navigator.platform[0] === 'M') ? posix : win32; + })(); + + //////////////////////////////////////////////////////////////////////////////////////////////////////// + // The is the "main" function which is to be prototyped // + // It run a small snippet in the jsx engine that // + // 1) Assigns $.__dirname with the value of the extensions __dirname base path // + // 2) Sets up a method $.__fileName() for retrieving from within the jsx script it's $.fileName value // + // more on that method later // + // At the end of the script the global declaration jsx = new Jsx(); has been made. // + // If you like you can remove that and include in your relevant functions // + // var jsx = new Jsx(); You would never call the Jsx function without the "new" declaration // + //////////////////////////////////////////////////////////////////////////////////////////////////////// + var Jsx = function() { + var jsxScript; + // Setup jsx function to enable the jsx scripts to easily retrieve their file location + jsxScript = [ + '$.level = 0;', + 'if(!$.__fileNames){', + ' $.__fileNames = {};', + ' $.__dirname = "__dirname__";'.replace('__dirname__', __dirname), + ' $.__fileName = function(name){', + ' name = name || $.fileName;', + ' return ($.__fileNames && $.__fileNames[name]) || $.fileName;', + ' };', + '}' + ].join(''); + evalScript(jsxScript); + return this; + }; + + /** + * [evalScript] For calling jsx scripts from the js engine + * + * The jsx.evalScript method is used for calling jsx scripts directly from the js engine + * Allows for easy replacement i.e. variable insertions and for forcing eval. + * For convenience jsx.eval or jsx.script or jsx.evalscript can be used instead of calling jsx.evalScript + * + * @param {String} jsxScript + * The string that makes up the jsx script + * it can contain a simple template like syntax for replacements + * 'alert("__foo__");' + * the __foo__ will be replaced as per the replacements parameter + * + * @param {Function} callback + * The callback function you want the jsx script to trigger on completion + * The result of the jsx script is passed as the argument to that function + * The function can exist in some other file. + * Note that InDesign does not automatically pass the callBack as a string. + * Either write your InDesign in a way that it returns a sting the form of + * return 'this is my result surrounded by quotes' + * or use the force eval option + * [Optional DEFAULT no callBack] + * + * @param {Object} replacements + * The replacements to make on the jsx script + * given the following script (template) + * 'alert("__message__: " + __val__);' + * and we want to change the script to + * 'alert("I was born in the year: " + 1234);' + * we would pass the following object + * {"message": 'I was born in the year', "val": 1234} + * or if not using reserved words like do we can leave out the key quotes + * {message: 'I was born in the year', val: 1234} + * [Optional DEFAULT no replacements] + * + * @param {Bolean} forceEval + * If the script should be wrapped in an eval and try catch + * This will 1) provide useful error feedback if heaven forbid it is needed + * 2) The result will be a string which is required for callback results in InDesign + * [Optional DEFAULT true] + * + * Note 1) The order of the parameters is irrelevant + * Note 2) One can pass the arguments as an object if desired + * jsx.evalScript(myCallBackFunction, 'alert("__myMessage__");', true); + * is the same as + * jsx.evalScript({ + * script: 'alert("__myMessage__");', + * replacements: {myMessage: 'Hi there'}, + * callBack: myCallBackFunction, + * eval: true + * }); + * note that either lower or camelCase key names are valid + * i.e. both callback or callBack will work + * + * The following keys are the same jsx || script || jsxScript || jsxscript || file + * The following keys are the same callBack || callback + * The following keys are the same replacements || replace + * The following keys are the same eval || forceEval || forceeval + * The following keys are the same forceEvalScript || forceevalscript || evalScript || evalscript; + * + * @return {Boolean} if the jsxScript was executed or not + */ + + Jsx.prototype.evalScript = function() { + var arg, i, key, replaceThis, withThis, args, callback, forceEval, replacements, jsxScript, isBin; + + ////////////////////////////////////////////////////////////////////////////////////// + // sort out order which arguments into jsxScript, callback, replacements, forceEval // + ////////////////////////////////////////////////////////////////////////////////////// + + args = arguments; + + // Detect if the parameters were passed as an object and if so allow for various keys + if (args.length === 1 && (arg = args[0]) instanceof Object) { + jsxScript = arg.jsxScript || arg.jsx || arg.script || arg.file || arg.jsxscript; + callback = arg.callBack || arg.callback; + replacements = arg.replacements || arg.replace; + forceEval = arg.eval || arg.forceEval || arg.forceeval; + } else { + for (i = 0; i < 4; i++) { + arg = args[i]; + if (arg === undefined) { + continue; + } + if (arg.constructor === String) { + jsxScript = arg; + continue; + } + if (arg.constructor === Object) { + replacements = arg; + continue; + } + if (arg.constructor === Function) { + callback = arg; + continue; + } + if (arg === false) { + forceEval = false; + } + } + } + + // If no script provide then not too much to do! + if (!jsxScript) { + return false; + } + + // Have changed the forceEval default to be true as I prefer the error handling + if (forceEval !== false) { + forceEval = true; + } + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // On Illustrator and other apps the result of the jsx script is automatically passed as a string // + // if you have a "script" containing the single number 1 and nothing else then the callBack will register as "1" // + // On InDesign that same script will provide a blank callBack // + // Let's say we have a callBack function var callBack = function(result){alert(result);} // + // On Ai your see the 1 in the alert // + // On ID your just see a blank alert // + // To see the 1 in the alert you need to convert the result to a string and then it will show // + // So if we rewrite out 1 byte script to '1' i.e. surround the 1 in quotes then the call back alert will show 1 // + // If the scripts planed one can make sure that the results always passed as a string (including errors) // + // otherwise one can wrap the script in an eval and then have the result passed as a string // + // I have not gone through all the apps but can say // + // for Ai you never need to set the forceEval to true // + // for ID you if you have not coded your script appropriately and your want to send a result to the callBack then set forceEval to true // + // I changed this that even on Illustrator it applies the try catch, Note the try catch will fail if $.level is set to 1 // + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + if (forceEval) { + + isBin = (jsxScript.substring(0, 10) === '@JSXBIN@ES') ? '' : '\n'; + jsxScript = ( + // "\n''') + '';} catch(e){(function(e){var n, a=[]; for (n in e){a.push(n + ': ' + e[n])}; return a.join('\n')})(e)}"); + // "\n''') + '';} catch(e){e + (e.line ? ('\\nLine ' + (+e.line - 1)) : '')}"); + [ + "$.level = 0;", + "try{eval('''" + isBin, // need to add an extra line otherwise #targetengine doesn't work ;-] + jsxScript.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/"/g, '\\"') + "\n''') + '';", + "} catch (e) {", + " (function(e) {", + " var line, sourceLine, name, description, ErrorMessage, fileName, start, end, bug;", + " line = +e.line" + (isBin === '' ? ';' : ' - 1;'), // To take into account the extra line added + " fileName = File(e.fileName).fsName;", + " sourceLine = line && e.source.split(/[\\r\\n]/)[line];", + " name = e.name;", + " description = e.description;", + " ErrorMessage = name + ' ' + e.number + ': ' + description;", + " if (fileName.length && !(/[\\/\\\\]\\d+$/.test(fileName))) {", + " ErrorMessage += '\\nFile: ' + fileName;", + " line++;", + " }", + " if (line){", + " ErrorMessage += '\\nLine: ' + line +", + " '-> ' + ((sourceLine.length < 300) ? sourceLine : sourceLine.substring(0,300) + '...');", + " }", + " if (e.start) {ErrorMessage += '\\nBug: ' + e.source.substring(e.start - 1, e.end)}", + " if ($.includeStack) {ErrorMessage += '\\nStack:' + $.stack;}", + " return ErrorMessage;", + " })(e);", + "}" + ].join('') + ); + + } + + ///////////////////////////////////////////////////////////// + // deal with the replacements // + // Note it's probably better to use ${template} `literals` // + ///////////////////////////////////////////////////////////// + + if (replacements) { + for (key in replacements) { + if (replacements.hasOwnProperty(key)) { + replaceThis = new RegExp('__' + key + '__', 'g'); + withThis = replacements[key]; + jsxScript = jsxScript.replace(replaceThis, withThis + ''); + } + } + } + + + try { + evalScript(jsxScript, callback); + return true; + } catch (err) { + //////////////////////////////////////////////// + // Do whatever error handling you want here ! // + //////////////////////////////////////////////// + var newErr; + newErr = new Error(err); + alert('Error Eek: ' + newErr.stack); + return false; + } + + }; + + + /** + * [evalFile] For calling jsx scripts from the js engine + * + * The jsx.evalFiles method is used for executing saved jsx scripts + * where the jsxScript parameter is a string of the jsx scripts file location. + * For convenience jsx.file or jsx.evalfile can be used instead of jsx.evalFile + * + * @param {String} file + * The path to jsx script + * If only the base name is provided then the path will be presumed to be the + * To execute files stored in the jsx folder located in the __dirname folder use + * jsx.evalFile('myFabJsxScript.jsx'); + * To execute files stored in the a folder myFabScripts located in the __dirname folder use + * jsx.evalFile('./myFabScripts/myFabJsxScript.jsx'); + * To execute files stored in the a folder myFabScripts located at an absolute url use + * jsx.evalFile('/Path/to/my/FabJsxScript.jsx'); (mac) + * or jsx.evalFile('C:Path/to/my/FabJsxScript.jsx'); (windows) + * + * @param {Function} callback + * The callback function you want the jsx script to trigger on completion + * The result of the jsx script is passed as the argument to that function + * The function can exist in some other file. + * Note that InDesign does not automatically pass the callBack as a string. + * Either write your InDesign in a way that it returns a sting the form of + * return 'this is my result surrounded by quotes' + * or use the force eval option + * [Optional DEFAULT no callBack] + * + * @param {Object} replacements + * The replacements to make on the jsx script + * give the following script (template) + * 'alert("__message__: " + __val__);' + * and we want to change the script to + * 'alert("I was born in the year: " + 1234);' + * we would pass the following object + * {"message": 'I was born in the year', "val": 1234} + * or if not using reserved words like do we can leave out the key quotes + * {message: 'I was born in the year', val: 1234} + * By default when possible the forceEvalScript will be set to true + * The forceEvalScript option cannot be true when there are replacements + * To force the forceEvalScript to be false you can send a blank set of replacements + * jsx.evalFile('myFabScript.jsx', {}); Will NOT be executed using the $.evalScript method + * jsx.evalFile('myFabScript.jsx'); Will YES be executed using the $.evalScript method + * see the forceEvalScript parameter for details on this + * [Optional DEFAULT no replacements] + * + * @param {Bolean} forceEval + * If the script should be wrapped in an eval and try catch + * This will 1) provide useful error feedback if heaven forbid it is needed + * 2) The result will be a string which is required for callback results in InDesign + * [Optional DEFAULT true] + * + * If no replacements are needed then the jsx script is be executed by using the $.evalFile method + * This exposes the true value of the $.fileName property + * In such a case it's best to avoid using the $.__fileName() with no base name as it won't work + * BUT one can still use the $.__fileName('baseName') method which is more accurate than the standard $.fileName property + * Let's say you have a Drive called "Graphics" AND YOU HAVE a root folder on your "main" drive called "Graphics" + * You call a script jsx.evalFile('/Volumes/Graphics/myFabScript.jsx'); + * $.fileName will give you '/Graphics/myFabScript.jsx' which is wrong + * $.__fileName('myFabScript.jsx') will give you '/Volumes/Graphics/myFabScript.jsx' which is correct + * $.__fileName() will not give you a reliable result + * Note that if your calling multiple versions of myFabScript.jsx stored in multiple folders then you can get stuffed! + * i.e. if the fileName is important to you then don't do that. + * It also will force the result of the jsx file as a string which is particularly useful for InDesign callBacks + * + * Note 1) The order of the parameters is irrelevant + * Note 2) One can pass the arguments as an object if desired + * jsx.evalScript(myCallBackFunction, 'alert("__myMessage__");', true); + * is the same as + * jsx.evalScript({ + * script: 'alert("__myMessage__");', + * replacements: {myMessage: 'Hi there'}, + * callBack: myCallBackFunction, + * eval: false, + * }); + * note that either lower or camelCase key names or valid + * i.e. both callback or callBack will work + * + * The following keys are the same file || jsx || script || jsxScript || jsxscript + * The following keys are the same callBack || callback + * The following keys are the same replacements || replace + * The following keys are the same eval || forceEval || forceeval + * + * @return {Boolean} if the jsxScript was executed or not + */ + + Jsx.prototype.evalFile = function() { + var arg, args, callback, fileName, fileNameScript, forceEval, forceEvalScript, + i, jsxFolder, jsxScript, newLine, replacements, success; + + success = true; // optimistic + args = arguments; + + jsxFolder = path.join(__dirname, 'jsx'); + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + // $.fileName does not return it's correct path in the jsx engine for files called from the js engine // + // In Illustrator it returns an integer in InDesign it returns an empty string // + // This script injection allows for the script to know it's path by calling // + // $.__fileName(); // + // on Illustrator this works pretty well // + // on InDesign it's best to use with a bit of care // + // If the a second script has been called the InDesing will "forget" the path to the first script // + // 2 work-arounds for this // + // 1) at the beginning of your script add var thePathToMeIs = $.fileName(); // + // thePathToMeIs will not be forgotten after running the second script // + // 2) $.__fileName('myBaseName.jsx'); // + // for example you have file with the following path // + // /path/to/me.jsx // + // Call $.__fileName('me.jsx') and you will get /path/to/me.jsx even after executing a second script // + // Note When the forceEvalScript option is used then you just use the regular $.fileName property // + ////////////////////////////////////////////////////////////////////////////////////////////////////////// + fileNameScript = [ + // The if statement should not normally be executed + 'if(!$.__fileNames){', + ' $.__fileNames = {};', + ' $.__dirname = "__dirname__";'.replace('__dirname__', __dirname), + ' $.__fileName = function(name){', + ' name = name || $.fileName;', + ' return ($.__fileNames && $.__fileNames[name]) || $.fileName;', + ' };', + '}', + '$.__fileNames["__basename__"] = $.__fileNames["" + $.fileName] = "__fileName__";' + ].join(''); + + ////////////////////////////////////////////////////////////////////////////////////// + // sort out order which arguments into jsxScript, callback, replacements, forceEval // + ////////////////////////////////////////////////////////////////////////////////////// + + + // Detect if the parameters were passed as an object and if so allow for various keys + if (args.length === 1 && (arg = args[0]) instanceof Object) { + jsxScript = arg.jsxScript || arg.jsx || arg.script || arg.file || arg.jsxscript; + callback = arg.callBack || arg.callback; + replacements = arg.replacements || arg.replace; + forceEval = arg.eval || arg.forceEval || arg.forceeval; + } else { + for (i = 0; i < 5; i++) { + arg = args[i]; + if (arg === undefined) { + continue; + } + if (arg.constructor.name === 'String') { + jsxScript = arg; + continue; + } + if (arg.constructor.name === 'Object') { + ////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // If no replacements are provided then the $.evalScript method will be used // + // This will allow directly for the $.fileName property to be used // + // If one does not want the $.evalScript method to be used then // + // either send a blank object as the replacements {} // + // or explicitly set the forceEvalScript option to false // + // This can only be done if the parameters are passed as an object // + // i.e. jsx.evalFile({file:'myFabScript.jsx', forceEvalScript: false}); // + // if the file was called using // + // i.e. jsx.evalFile('myFabScript.jsx'); // + // then the following jsx code is called $.evalFile(new File('Path/to/myFabScript.jsx', 10000000000)) + ''; // + // forceEval is never needed if the forceEvalScript is triggered // + ////////////////////////////////////////////////////////////////////////////////////////////////////////////// + replacements = arg; + continue; + } + if (arg.constructor === Function) { + callback = arg; + continue; + } + if (arg === false) { + forceEval = false; + } + } + } + + // If no script provide then not too much to do! + if (!jsxScript) { + return false; + } + + forceEvalScript = !replacements; + + + ////////////////////////////////////////////////////// + // Get path of script // + // Check if it's literal, relative or in jsx folder // + ////////////////////////////////////////////////////// + + if (/^\/|[a-zA-Z]+:/.test(jsxScript)) { // absolute path Mac | Windows + jsxScript = path.normalize(jsxScript); + } else if (/^\.+\//.test(jsxScript)) { + jsxScript = path.join(__dirname, jsxScript); // relative path + } else { + jsxScript = path.join(jsxFolder, jsxScript); // files in the jsxFolder + } + + if (forceEvalScript) { + jsxScript = jsxScript.replace(/"/g, '\\"'); + // Check that the path exist, should change this to asynchronous at some point + if (!window.cep.fs.stat(jsxScript).err) { + jsxScript = fileNameScript.replace(/__fileName__/, jsxScript).replace(/__basename__/, path.basename(jsxScript)) + + '$.evalFile(new File("' + jsxScript.replace(/\\/g, '\\\\') + '")) + "";'; + return this.evalScript(jsxScript, callback, forceEval); + } else { + throw new Error(`The file: {jsxScript} could not be found / read`); + } + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + // Replacements made so we can't use $.evalFile and need to read the jsx script for ourselves // + //////////////////////////////////////////////////////////////////////////////////////////////// + + fileName = jsxScript.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); + try { + jsxScript = window.cep.fs.readFile(jsxScript).data; + } catch (er) { + throw new Error(`The file: ${fileName} could not be read`); + } + // It is desirable that the injected fileNameScript is on the same line as the 1st line of the script + // This is so that the $.line or error.line returns the same value as the actual file + // However if the 1st line contains a # directive then we need to insert a new line and stuff the above problem + // When possible i.e. when there's no replacements then $.evalFile will be used and then the whole issue is avoided + newLine = /^\s*#/.test(jsxScript) ? '\n' : ''; + jsxScript = fileNameScript.replace(/__fileName__/, fileName).replace(/__basename__/, path.basename(fileName)) + newLine + jsxScript; + + try { + // evalScript(jsxScript, callback); + return this.evalScript(jsxScript, callback, replacements, forceEval); + } catch (err) { + //////////////////////////////////////////////// + // Do whatever error handling you want here ! // + //////////////////////////////////////////////// + var newErr; + newErr = new Error(err); + alert('Error Eek: ' + newErr.stack); + return false; + } + + return success; // success should be an array but for now it's a Boolean + }; + + + //////////////////////////////////// + // Setup alternative method names // + //////////////////////////////////// + Jsx.prototype.eval = Jsx.prototype.script = Jsx.prototype.evalscript = Jsx.prototype.evalScript; + Jsx.prototype.file = Jsx.prototype.evalfile = Jsx.prototype.evalFile; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Examples // + // jsx.evalScript('alert("foo");'); // + // jsx.evalFile('foo.jsx'); // where foo.jsx is stored in the jsx folder at the base of the extensions directory // + // jsx.evalFile('../myFolder/foo.jsx'); // where a relative or absolute file path is given // + // // + // using conventional methods one would use in the case were the values to swap were supplied by variables // + // csInterface.evalScript('var q = "' + name + '"; alert("' + myString + '" ' + myOp + ' q);q;', callback); // + // Using all the '' + foo + '' is very error prone // + // jsx.evalScript('var q = "__name__"; alert(__string__ __opp__ q);q;',{'name':'Fred', 'string':'Hello ', 'opp':'+'}, callBack); // + // is much simpler and less error prone // + // // + // more readable to use object // + // jsx.evalFile({ // + // file: 'yetAnotherFabScript.jsx', // + // replacements: {"this": foo, That: bar, and: "&&", the: foo2, other: bar2}, // + // eval: true // + // }) // + // Enjoy // + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + + jsx = new Jsx(); +})(); diff --git a/openpype/hosts/photoshop/api/extension/host/index.jsx b/openpype/hosts/photoshop/api/extension/host/index.jsx new file mode 100644 index 00000000000..2acec1ebc13 --- /dev/null +++ b/openpype/hosts/photoshop/api/extension/host/index.jsx @@ -0,0 +1,484 @@ +#include "json.js"; +#target photoshop + +var LogFactory=function(file,write,store,level,defaultStatus,continuing){if(file&&(file.constructor===String||file.constructor===File)){file={file:file};}else if(!file)file={file:{}};write=(file.write!==undefined)?file.write:write;if(write===undefined){write=true;}store=(file.store!==undefined)?file.store||false:store||false;level=(file.level!==undefined)?file.level:level;defaultStatus=(file.defaultStatus!==undefined)?file.defaultStatus:defaultStatus;if(defaultStatus===undefined){defaultStatus='LOG';}continuing=(file.continuing!==undefined)?file.continuing:continuing||false;file=file.file||{};var stack,times,logTime,logPoint,icons,statuses,LOG_LEVEL,LOG_STATUS;stack=[];times=[];logTime=new Date();logPoint='Log Factory Start';icons={"1":"\ud83d\udd50","130":"\ud83d\udd5c","2":"\ud83d\udd51","230":"\ud83d\udd5d","3":"\ud83d\udd52","330":"\ud83d\udd5e","4":"\ud83d\udd53","430":"\ud83d\udd5f","5":"\ud83d\udd54","530":"\ud83d\udd60","6":"\ud83d\udd55","630":"\ud83d\udd61","7":"\ud83d\udd56","730":"\ud83d\udd62","8":"\ud83d\udd57","830":"\ud83d\udd63","9":"\ud83d\udd58","930":"\ud83d\udd64","10":"\ud83d\udd59","1030":"\ud83d\udd65","11":"\ud83d\udd5a","1130":"\ud83d\udd66","12":"\ud83d\udd5b","1230":"\ud83d\udd67","AIRPLANE":"\ud83d\udee9","ALARM":"\u23f0","AMBULANCE":"\ud83d\ude91","ANCHOR":"\u2693","ANGRY":"\ud83d\ude20","ANGUISHED":"\ud83d\ude27","ANT":"\ud83d\udc1c","ANTENNA":"\ud83d\udce1","APPLE":"\ud83c\udf4f","APPLE2":"\ud83c\udf4e","ATM":"\ud83c\udfe7","ATOM":"\u269b","BABYBOTTLE":"\ud83c\udf7c","BAD:":"\ud83d\udc4e","BANANA":"\ud83c\udf4c","BANDAGE":"\ud83e\udd15","BANK":"\ud83c\udfe6","BATTERY":"\ud83d\udd0b","BED":"\ud83d\udecf","BEE":"\ud83d\udc1d","BEER":"\ud83c\udf7a","BELL":"\ud83d\udd14","BELLOFF":"\ud83d\udd15","BIRD":"\ud83d\udc26","BLACKFLAG":"\ud83c\udff4","BLUSH":"\ud83d\ude0a","BOMB":"\ud83d\udca3","BOOK":"\ud83d\udcd5","BOOKMARK":"\ud83d\udd16","BOOKS":"\ud83d\udcda","BOW":"\ud83c\udff9","BOWLING":"\ud83c\udfb3","BRIEFCASE":"\ud83d\udcbc","BROKEN":"\ud83d\udc94","BUG":"\ud83d\udc1b","BUILDING":"\ud83c\udfdb","BUILDINGS":"\ud83c\udfd8","BULB":"\ud83d\udca1","BUS":"\ud83d\ude8c","CACTUS":"\ud83c\udf35","CALENDAR":"\ud83d\udcc5","CAMEL":"\ud83d\udc2a","CAMERA":"\ud83d\udcf7","CANDLE":"\ud83d\udd6f","CAR":"\ud83d\ude98","CAROUSEL":"\ud83c\udfa0","CASTLE":"\ud83c\udff0","CATEYES":"\ud83d\ude3b","CATJOY":"\ud83d\ude39","CATMOUTH":"\ud83d\ude3a","CATSMILE":"\ud83d\ude3c","CD":"\ud83d\udcbf","CHECK":"\u2714","CHEQFLAG":"\ud83c\udfc1","CHICK":"\ud83d\udc25","CHICKEN":"\ud83d\udc14","CHICKHEAD":"\ud83d\udc24","CIRCLEBLACK":"\u26ab","CIRCLEBLUE":"\ud83d\udd35","CIRCLERED":"\ud83d\udd34","CIRCLEWHITE":"\u26aa","CIRCUS":"\ud83c\udfaa","CLAPPER":"\ud83c\udfac","CLAPPING":"\ud83d\udc4f","CLIP":"\ud83d\udcce","CLIPBOARD":"\ud83d\udccb","CLOUD":"\ud83c\udf28","CLOVER":"\ud83c\udf40","CLOWN":"\ud83e\udd21","COLDSWEAT":"\ud83d\ude13","COLDSWEAT2":"\ud83d\ude30","COMPRESS":"\ud83d\udddc","CONFOUNDED":"\ud83d\ude16","CONFUSED":"\ud83d\ude15","CONSTRUCTION":"\ud83d\udea7","CONTROL":"\ud83c\udf9b","COOKIE":"\ud83c\udf6a","COOKING":"\ud83c\udf73","COOL":"\ud83d\ude0e","COOLBOX":"\ud83c\udd92","COPYRIGHT":"\u00a9","CRANE":"\ud83c\udfd7","CRAYON":"\ud83d\udd8d","CREDITCARD":"\ud83d\udcb3","CROSS":"\u2716","CROSSBOX:":"\u274e","CRY":"\ud83d\ude22","CRYCAT":"\ud83d\ude3f","CRYSTALBALL":"\ud83d\udd2e","CUSTOMS":"\ud83d\udec3","DELICIOUS":"\ud83d\ude0b","DERELICT":"\ud83c\udfda","DESKTOP":"\ud83d\udda5","DIAMONDLB":"\ud83d\udd37","DIAMONDLO":"\ud83d\udd36","DIAMONDSB":"\ud83d\udd39","DIAMONDSO":"\ud83d\udd38","DICE":"\ud83c\udfb2","DISAPPOINTED":"\ud83d\ude1e","CRY2":"\ud83d\ude25","DIVISION":"\u2797","DIZZY":"\ud83d\ude35","DOLLAR":"\ud83d\udcb5","DOLLAR2":"\ud83d\udcb2","DOWNARROW":"\u2b07","DVD":"\ud83d\udcc0","EJECT":"\u23cf","ELEPHANT":"\ud83d\udc18","EMAIL":"\ud83d\udce7","ENVELOPE":"\ud83d\udce8","ENVELOPE2":"\u2709","ENVELOPE_DOWN":"\ud83d\udce9","EURO":"\ud83d\udcb6","EVIL":"\ud83d\ude08","EXPRESSIONLESS":"\ud83d\ude11","EYES":"\ud83d\udc40","FACTORY":"\ud83c\udfed","FAX":"\ud83d\udce0","FEARFUL":"\ud83d\ude28","FILEBOX":"\ud83d\uddc3","FILECABINET":"\ud83d\uddc4","FIRE":"\ud83d\udd25","FIREENGINE":"\ud83d\ude92","FIST":"\ud83d\udc4a","FLOWER":"\ud83c\udf37","FLOWER2":"\ud83c\udf38","FLUSHED":"\ud83d\ude33","FOLDER":"\ud83d\udcc1","FOLDER2":"\ud83d\udcc2","FREE":"\ud83c\udd93","FROG":"\ud83d\udc38","FROWN":"\ud83d\ude41","GEAR":"\u2699","GLOBE":"\ud83c\udf0d","GLOWINGSTAR":"\ud83c\udf1f","GOOD:":"\ud83d\udc4d","GRIMACING":"\ud83d\ude2c","GRIN":"\ud83d\ude00","GRINNINGCAT":"\ud83d\ude38","HALO":"\ud83d\ude07","HAMMER":"\ud83d\udd28","HAMSTER":"\ud83d\udc39","HAND":"\u270b","HANDDOWN":"\ud83d\udc47","HANDLEFT":"\ud83d\udc48","HANDRIGHT":"\ud83d\udc49","HANDUP":"\ud83d\udc46","HATCHING":"\ud83d\udc23","HAZARD":"\u2623","HEADPHONE":"\ud83c\udfa7","HEARNOEVIL":"\ud83d\ude49","HEARTBLUE":"\ud83d\udc99","HEARTEYES":"\ud83d\ude0d","HEARTGREEN":"\ud83d\udc9a","HEARTYELLOW":"\ud83d\udc9b","HELICOPTER":"\ud83d\ude81","HERB":"\ud83c\udf3f","HIGH_BRIGHTNESS":"\ud83d\udd06","HIGHVOLTAGE":"\u26a1","HIT":"\ud83c\udfaf","HONEY":"\ud83c\udf6f","HOT":"\ud83c\udf36","HOURGLASS":"\u23f3","HOUSE":"\ud83c\udfe0","HUGGINGFACE":"\ud83e\udd17","HUNDRED":"\ud83d\udcaf","HUSHED":"\ud83d\ude2f","ID":"\ud83c\udd94","INBOX":"\ud83d\udce5","INDEX":"\ud83d\uddc2","JOY":"\ud83d\ude02","KEY":"\ud83d\udd11","KISS":"\ud83d\ude18","KISS2":"\ud83d\ude17","KISS3":"\ud83d\ude19","KISS4":"\ud83d\ude1a","KISSINGCAT":"\ud83d\ude3d","KNIFE":"\ud83d\udd2a","LABEL":"\ud83c\udff7","LADYBIRD":"\ud83d\udc1e","LANDING":"\ud83d\udeec","LAPTOP":"\ud83d\udcbb","LEFTARROW":"\u2b05","LEMON":"\ud83c\udf4b","LIGHTNINGCLOUD":"\ud83c\udf29","LINK":"\ud83d\udd17","LITTER":"\ud83d\udeae","LOCK":"\ud83d\udd12","LOLLIPOP":"\ud83c\udf6d","LOUDSPEAKER":"\ud83d\udce2","LOW_BRIGHTNESS":"\ud83d\udd05","MAD":"\ud83d\ude1c","MAGNIFYING_GLASS":"\ud83d\udd0d","MASK":"\ud83d\ude37","MEDAL":"\ud83c\udf96","MEMO":"\ud83d\udcdd","MIC":"\ud83c\udfa4","MICROSCOPE":"\ud83d\udd2c","MINUS":"\u2796","MOBILE":"\ud83d\udcf1","MONEY":"\ud83d\udcb0","MONEYMOUTH":"\ud83e\udd11","MONKEY":"\ud83d\udc35","MOUSE":"\ud83d\udc2d","MOUSE2":"\ud83d\udc01","MOUTHLESS":"\ud83d\ude36","MOVIE":"\ud83c\udfa5","MUGS":"\ud83c\udf7b","NERD":"\ud83e\udd13","NEUTRAL":"\ud83d\ude10","NEW":"\ud83c\udd95","NOENTRY":"\ud83d\udeab","NOTEBOOK":"\ud83d\udcd4","NOTEPAD":"\ud83d\uddd2","NUTANDBOLT":"\ud83d\udd29","O":"\u2b55","OFFICE":"\ud83c\udfe2","OK":"\ud83c\udd97","OKHAND":"\ud83d\udc4c","OLDKEY":"\ud83d\udddd","OPENLOCK":"\ud83d\udd13","OPENMOUTH":"\ud83d\ude2e","OUTBOX":"\ud83d\udce4","PACKAGE":"\ud83d\udce6","PAGE":"\ud83d\udcc4","PAINTBRUSH":"\ud83d\udd8c","PALETTE":"\ud83c\udfa8","PANDA":"\ud83d\udc3c","PASSPORT":"\ud83d\udec2","PAWS":"\ud83d\udc3e","PEN":"\ud83d\udd8a","PEN2":"\ud83d\udd8b","PENSIVE":"\ud83d\ude14","PERFORMING":"\ud83c\udfad","PHONE":"\ud83d\udcde","PILL":"\ud83d\udc8a","PING":"\u2757","PLATE":"\ud83c\udf7d","PLUG":"\ud83d\udd0c","PLUS":"\u2795","POLICE":"\ud83d\ude93","POLICELIGHT":"\ud83d\udea8","POSTOFFICE":"\ud83c\udfe4","POUND":"\ud83d\udcb7","POUTING":"\ud83d\ude21","POUTINGCAT":"\ud83d\ude3e","PRESENT":"\ud83c\udf81","PRINTER":"\ud83d\udda8","PROJECTOR":"\ud83d\udcfd","PUSHPIN":"\ud83d\udccc","QUESTION":"\u2753","RABBIT":"\ud83d\udc30","RADIOACTIVE":"\u2622","RADIOBUTTON":"\ud83d\udd18","RAINCLOUD":"\ud83c\udf27","RAT":"\ud83d\udc00","RECYCLE":"\u267b","REGISTERED":"\u00ae","RELIEVED":"\ud83d\ude0c","ROBOT":"\ud83e\udd16","ROCKET":"\ud83d\ude80","ROLLING":"\ud83d\ude44","ROOSTER":"\ud83d\udc13","RULER":"\ud83d\udccf","SATELLITE":"\ud83d\udef0","SAVE":"\ud83d\udcbe","SCHOOL":"\ud83c\udfeb","SCISSORS":"\u2702","SCREAMING":"\ud83d\ude31","SCROLL":"\ud83d\udcdc","SEAT":"\ud83d\udcba","SEEDLING":"\ud83c\udf31","SEENOEVIL":"\ud83d\ude48","SHIELD":"\ud83d\udee1","SHIP":"\ud83d\udea2","SHOCKED":"\ud83d\ude32","SHOWER":"\ud83d\udebf","SLEEPING":"\ud83d\ude34","SLEEPY":"\ud83d\ude2a","SLIDER":"\ud83c\udf9a","SLOT":"\ud83c\udfb0","SMILE":"\ud83d\ude42","SMILING":"\ud83d\ude03","SMILINGCLOSEDEYES":"\ud83d\ude06","SMILINGEYES":"\ud83d\ude04","SMILINGSWEAT":"\ud83d\ude05","SMIRK":"\ud83d\ude0f","SNAIL":"\ud83d\udc0c","SNAKE":"\ud83d\udc0d","SOCCER":"\u26bd","SOS":"\ud83c\udd98","SPEAKER":"\ud83d\udd08","SPEAKEROFF":"\ud83d\udd07","SPEAKNOEVIL":"\ud83d\ude4a","SPIDER":"\ud83d\udd77","SPIDERWEB":"\ud83d\udd78","STAR":"\u2b50","STOP":"\u26d4","STOPWATCH":"\u23f1","SULK":"\ud83d\ude26","SUNFLOWER":"\ud83c\udf3b","SUNGLASSES":"\ud83d\udd76","SYRINGE":"\ud83d\udc89","TAKEOFF":"\ud83d\udeeb","TAXI":"\ud83d\ude95","TELESCOPE":"\ud83d\udd2d","TEMPORATURE":"\ud83e\udd12","TENNIS":"\ud83c\udfbe","THERMOMETER":"\ud83c\udf21","THINKING":"\ud83e\udd14","THUNDERCLOUD":"\u26c8","TICKBOX":"\u2705","TICKET":"\ud83c\udf9f","TIRED":"\ud83d\ude2b","TOILET":"\ud83d\udebd","TOMATO":"\ud83c\udf45","TONGUE":"\ud83d\ude1b","TOOLS":"\ud83d\udee0","TORCH":"\ud83d\udd26","TORNADO":"\ud83c\udf2a","TOUNG2":"\ud83d\ude1d","TRADEMARK":"\u2122","TRAFFICLIGHT":"\ud83d\udea6","TRASH":"\ud83d\uddd1","TREE":"\ud83c\udf32","TRIANGLE_LEFT":"\u25c0","TRIANGLE_RIGHT":"\u25b6","TRIANGLEDOWN":"\ud83d\udd3b","TRIANGLEUP":"\ud83d\udd3a","TRIANGULARFLAG":"\ud83d\udea9","TROPHY":"\ud83c\udfc6","TRUCK":"\ud83d\ude9a","TRUMPET":"\ud83c\udfba","TURKEY":"\ud83e\udd83","TURTLE":"\ud83d\udc22","UMBRELLA":"\u26f1","UNAMUSED":"\ud83d\ude12","UPARROW":"\u2b06","UPSIDEDOWN":"\ud83d\ude43","WARNING":"\u26a0","WATCH":"\u231a","WAVING":"\ud83d\udc4b","WEARY":"\ud83d\ude29","WEARYCAT":"\ud83d\ude40","WHITEFLAG":"\ud83c\udff3","WINEGLASS":"\ud83c\udf77","WINK":"\ud83d\ude09","WORRIED":"\ud83d\ude1f","WRENCH":"\ud83d\udd27","X":"\u274c","YEN":"\ud83d\udcb4","ZIPPERFACE":"\ud83e\udd10","UNDEFINED":"","":""};statuses={F:'FATAL',B:'BUG',C:'CRITICAL',E:'ERROR',W:'WARNING',I:'INFO',IM:'IMPORTANT',D:'DEBUG',L:'LOG',CO:'CONSTANT',FU:'FUNCTION',R:'RETURN',V:'VARIABLE',S:'STACK',RE:'RESULT',ST:'STOPPER',TI:'TIMER',T:'TRACE'};LOG_LEVEL={NONE:7,OFF:7,FATAL:6,ERROR:5,WARN:4,INFO:3,UNDEFINED:2,'':2,DEFAULT:2,DEBUG:2,TRACE:1,ON:0,ALL:0,};LOG_STATUS={OFF:LOG_LEVEL.OFF,NONE:LOG_LEVEL.OFF,NO:LOG_LEVEL.OFF,NOPE:LOG_LEVEL.OFF,FALSE:LOG_LEVEL.OFF,FATAL:LOG_LEVEL.FATAL,BUG:LOG_LEVEL.ERROR,CRITICAL:LOG_LEVEL.ERROR,ERROR:LOG_LEVEL.ERROR,WARNING:LOG_LEVEL.WARN,INFO:LOG_LEVEL.INFO,IMPORTANT:LOG_LEVEL.INFO,DEBUG:LOG_LEVEL.DEBUG,LOG:LOG_LEVEL.DEBUG,STACK:LOG_LEVEL.DEBUG,CONSTANT:LOG_LEVEL.DEBUG,FUNCTION:LOG_LEVEL.DEBUG,VARIABLE:LOG_LEVEL.DEBUG,RETURN:LOG_LEVEL.DEBUG,RESULT:LOG_LEVEL.TRACE,STOPPER:LOG_LEVEL.TRACE,TIMER:LOG_LEVEL.TRACE,TRACE:LOG_LEVEL.TRACE,ALL:LOG_LEVEL.ALL,YES:LOG_LEVEL.ALL,YEP:LOG_LEVEL.ALL,TRUE:LOG_LEVEL.ALL};var logFile,logFolder;var LOG=function(message,status,icon){if(LOG.level!==LOG_LEVEL.OFF&&(LOG.write||LOG.store)&&LOG.arguments.length)return LOG.addMessage(message,status,icon);};LOG.logDecodeLevel=function(level){if(level==~~level)return Math.abs(level);var lev;level+='';level=level.toUpperCase();if(level in statuses){level=statuses[level];}lev=LOG_LEVEL[level];if(lev!==undefined)return lev;lev=LOG_STATUS[level];if(lev!==undefined)return lev;return LOG_LEVEL.DEFAULT;};LOG.write=write;LOG.store=store;LOG.level=LOG.logDecodeLevel(level);LOG.status=defaultStatus;LOG.addMessage=function(message,status,icon){var date=new Date(),count,bool,logStatus;if(status&&status.constructor.name==='String'){status=status.toUpperCase();status=statuses[status]||status;}else status=LOG.status;logStatus=LOG_STATUS[status]||LOG_STATUS.ALL;if(logStatus999)?'['+LOG.count+'] ':(' ['+LOG.count+'] ').slice(-7);message=count+status+icon+(message instanceof Object?message.toSource():message)+date;if(LOG.store){stack.push(message);}if(LOG.write){bool=file&&file.writable&&logFile.writeln(message);if(!bool){file.writable=true;LOG.setFile(logFile);logFile.writeln(message);}}LOG.count++;return true;};var logNewFile=function(file,isCookie,overwrite){file.encoding='UTF-8';file.lineFeed=($.os[0]=='M')?'Macintosh':' Windows';if(isCookie)return file.open(overwrite?'w':'e')&&file;file.writable=LOG.write;logFile=file;logFolder=file.parent;if(continuing){LOG.count=LOG.setCount(file);}return(!LOG.write&&file||(file.open('a')&&file));};LOG.setFile=function(file,isCookie,overwrite){var bool,folder,fileName,suffix,newFileName,f,d,safeFileName;d=new Date();f=$.stack.split("\n")[0].replace(/^\[\(?/,'').replace(/\)?\]$/,'');if(f==~~f){f=$.fileName.replace(/[^\/]+\//g,'');}safeFileName=File.encode((isCookie?'/COOKIE_':'/LOG_')+f.replace(/^\//,'')+'_'+(1900+d.getYear())+(''+d).replace(/...(...)(..).+/,'_$1_$2')+(isCookie?'.txt':'.log'));if(file&&file.constructor.name=='String'){file=(file.match('/'))?new File(file):new File((logFolder||Folder.temp)+'/'+file);}if(file instanceof File){folder=file.parent;bool=folder.exists||folder.create();if(!bool)folder=Folder.temp;fileName=File.decode(file.name);suffix=fileName.match(/\.[^.]+$/);suffix=suffix?suffix[0]:'';fileName='/'+fileName;newFileName=fileName.replace(/\.[^.]+$/,'')+'_'+(+(new Date())+suffix);f=logNewFile(file,isCookie,overwrite);if(f)return f;f=logNewFile(new File(folder+newFileName),isCookie,overwrite);if(f)return f;f=logNewFile(new File(folder+safeFileName),isCookie,overwrite);if(f)return f;if(folder!=Folder.temp){f=logNewFile(new File(Folder.temp+fileName),isCookie,overwrite);if(f)return f;f=logNewFile(new File(Folder.temp+safeFileName),isCookie,overwrite);return f||new File(Folder.temp+safeFileName);}}return LOG.setFile(((logFile&&!isCookie)?new File(logFile):new File(Folder.temp+safeFileName)),isCookie,overwrite );};LOG.setCount=function(file){if(~~file===file){LOG.count=file;return LOG.count;}if(file===undefined){file=logFile;}if(file&&file.constructor===String){file=new File(file);}var logNumbers,contents;if(!file.length||!file.exists){LOG.count=1;return 1;}file.open('r');file.encoding='utf-8';file.seek(10000,2);contents='\n'+file.read();logNumbers=contents.match(/\n{0,3}\[\d+\] \[\w+\]+/g);if(logNumbers){logNumbers=+logNumbers[logNumbers.length-1].match(/\d+/)+1;file.close();LOG.count=logNumbers;return logNumbers;}if(file.length<10001){file.close();LOG.count=1;return 1;}file.seek(10000000,2);contents='\n'+file.read();logNumbers=contents.match(/\n{0,3}\[\d+\] \[\w+\]+/g);if(logNumbers){logNumbers=+logNumbers[logNumbers.length-1].match(/\d+/)+1;file.close();LOG.count=logNumbers;return logNumbers;}file.close();LOG.count=1;return 1;};LOG.setLevel=function(level){LOG.level=LOG.logDecodeLevel(level);return LOG.level;};LOG.setStatus=function(status){status=(''+status).toUpperCase();LOG.status=statuses[status]||status;return LOG.status;};LOG.cookie=function(file,level,overwrite,setLevel){var log,cookie;if(!file){file={file:file};}if(file&&(file.constructor===String||file.constructor===File)){file={file:file};}log=file;if(log.level===undefined){log.level=(level!==undefined)?level:'NONE';}if(log.overwrite===undefined){log.overwrite=(overwrite!==undefined)?overwrite:false;}if(log.setLevel===undefined){log.setLevel=(setLevel!==undefined)?setLevel:true;}setLevel=log.setLevel;overwrite=log.overwrite;level=log.level;file=log.file;file=LOG.setFile(file,true,overwrite);if(overwrite){file.write(level);}else{cookie=file.read();if(cookie.length){level=cookie;}else{file.write(level);}}file.close();if(setLevel){LOG.setLevel(level);}return{path:file,level:level};};LOG.args=function(args,funct,line){if(LOG.level>LOG_STATUS.FUNCTION)return;if(!(args&&(''+args.constructor).replace(/\s+/g,'')==='functionObject(){[nativecode]}'))return;if(!LOG.args.STRIP_COMMENTS){LOG.args.STRIP_COMMENTS=/((\/.*$)|(\/\*[\s\S]*?\*\/))/mg;}if(!LOG.args.ARGUMENT_NAMES){LOG.args.ARGUMENT_NAMES=/([^\s,]+)/g;}if(!LOG.args.OUTER_BRACKETS){LOG.args.OUTER_BRACKETS=/^\((.+)?\)$/;}if(!LOG.args.NEW_SOMETHING){LOG.args.NEW_SOMETHING=/^new \w+\((.+)?\)$/;}var functionString,argumentNames,stackInfo,report,functionName,arg,argsL,n,argName,argValue,argsTotal;if(funct===~~funct){line=funct;}if(!(funct instanceof Function)){funct=args.callee;}if(!(funct instanceof Function))return;functionName=funct.name;functionString=(''+funct).replace(LOG.args.STRIP_COMMENTS,'');argumentNames=functionString.slice(functionString.indexOf('(')+1,functionString.indexOf(')')).match(LOG.args.ARGUMENT_NAMES);argumentNames=argumentNames||[];report=[];report.push('--------------');report.push('Function Data:');report.push('--------------');report.push('Function Name:'+functionName);argsL=args.length;stackInfo=$.stack.split(/[\n\r]/);stackInfo.pop();stackInfo=stackInfo.join('\n ');report.push('Call stack:'+stackInfo);if(line){report.push('Function Line around:'+line);}report.push('Arguments Provided:'+argsL);report.push('Named Arguments:'+argumentNames.length);if(argumentNames.length){report.push('Arguments Names:'+argumentNames.join(','));}if(argsL){report.push('----------------');report.push('Argument Values:');report.push('----------------');}argsTotal=Math.max(argsL,argumentNames.length);for(n=0;n=argsL){argValue='NO VALUE PROVIDED';}else if(arg===undefined){argValue='undefined';}else if(arg===null){argValue='null';}else{argValue=arg.toSource().replace(LOG.args.OUTER_BRACKETS,'$1').replace(LOG.args.NEW_SOMETHING,'$1');}report.push((argName?argName:'arguments['+n+']')+':'+argValue);}report.push('');report=report.join('\n ');LOG(report,'f');return report;};LOG.stack=function(reverse){var st=$.stack.split('\n');st.pop();st.pop();if(reverse){st.reverse();}return LOG(st.join('\n '),'s');};LOG.values=function(values){var n,value,map=[];if(!(values instanceof Object||values instanceof Array)){return;}if(!LOG.values.OUTER_BRACKETS){LOG.values.OUTER_BRACKETS=/^\((.+)?\)$/;}if(!LOG.values.NEW_SOMETHING){LOG.values.NEW_SOMETHING=/^new \w+\((.+)?\)$/;}for(n in values){try{value=values[n];if(value===undefined){value='undefined';}else if(value===null){value='null';}else{value=value.toSource().replace(LOG.values.OUTER_BRACKETS,'$1').replace(LOG.values.NEW_SOMETHING,'$1');}}catch(e){value='\uD83D\uDEAB '+e;}map.push(n+':'+value);}if(map.length){map=map.join('\n ')+'\n ';return LOG(map,'v');}};LOG.reset=function(all){stack.length=0;LOG.count=1;if(all!==false){if(logFile instanceof File){logFile.close();}logFile=LOG.store=LOG.writeToFile=undefined;LOG.write=true;logFolder=Folder.temp;logTime=new Date();logPoint='After Log Reset';}};LOG.stopper=function(message){var newLogTime,t,m,newLogPoint;newLogTime=new Date();newLogPoint=(LOG.count!==undefined)?'LOG#'+LOG.count:'BEFORE LOG#1';LOG.time=t=newLogTime-logTime;if(message===false){return;}message=message||'Stopper start point';t=LOG.prettyTime(t);m=message+'\n '+'From '+logPoint+' to '+newLogPoint+' took '+t+' Starting '+logTime+' '+logTime.getMilliseconds()+'ms'+' Ending '+newLogTime+' '+newLogTime.getMilliseconds()+'ms';LOG(m,'st');logPoint=newLogPoint;logTime=newLogTime;return m;};LOG.start=function(message){var t=new Date();times.push([t,(message!==undefined)?message+'':'']);};LOG.stop=function(message){if(!times.length)return;message=(message)?message+' ':'';var nt,startLog,ot,om,td,m;nt=new Date();startLog=times.pop();ot=startLog[0];om=startLog[1];td=nt-ot;if(om.length){om+=' ';}m=om+'STARTED ['+ot+' '+ot.getMilliseconds()+'ms]\n '+message+'FINISHED ['+nt+' '+nt.getMilliseconds()+'ms]\n TOTAL TIME ['+LOG.prettyTime(td)+']';LOG(m,'ti');return m;};LOG.prettyTime=function(t){var h,m,s,ms;h=Math.floor(t / 3600000);m=Math.floor((t % 3600000)/ 60000);s=Math.floor((t % 60000)/ 1000);ms=t % 1000;t=(!t)?'<1ms':((h)?h+' hours ':'')+((m)?m+' minutes ':'')+((s)?s+' seconds ':'')+((ms&&(h||m||s))?'&':'')+((ms)?ms+'ms':'');return t;};LOG.get=function(){if(!stack.length)return 'THE LOG IS NOT SET TO STORE';var a=fetchLogLines(arguments);return a?'\n'+a.join('\n'):'NO LOGS AVAILABLE';};var fetchLogLines=function(){var args=arguments[0];if(!args.length)return stack;var c,n,l,a=[],ln,start,end,j,sl;l=args.length;sl=stack.length-1;n=0;for(c=0;cln)?sl+ln+1:ln-1;if(ln>=0&&ln<=sl)a[n++]=stack[ln];}else if(ln instanceof Array&&ln.length===2){start=ln[0];end=ln[1];if(!(~~start===start&&~~end===end))continue;start=(0>start)?sl+start+1:start-1;end=(0>end)?sl+end+1:end-1;start=Math.max(Math.min(sl,start),0);end=Math.min(Math.max(end,0),sl);if(start<=end)for(j=start;j<=end;j++)a[n++]=stack[j];else for(j=start;j>=end;j--)a[n++]=stack[j];}}return(n)?a:false;};LOG.file=function(){return logFile;};LOG.openFolder=function(){if(logFolder)return logFolder.execute();};LOG.show=LOG.execute=function(){if(logFile)return logFile.execute();};LOG.close=function(){if(logFile)return logFile.close();};LOG.setFile(file);if(!$.summary.difference){$.summary.difference=function(){return $.summary().replace(/ *([0-9]+)([^ ]+)(\n?)/g,$.summary.updateSnapshot );};}if(!$.summary.updateSnapshot){$.summary.updateSnapshot=function(full,count,name,lf){var snapshot=$.summary.snapshot;count=Number(count);var prev=snapshot[name]?snapshot[name]:0;snapshot[name]=count;var diff=count-prev;if(diff===0)return "";return " ".substring(String(diff).length)+diff+" "+name+lf;};}if(!$.summary.snapshot){$.summary.snapshot=[];$.summary.difference();}$.gc();$.gc();$.summary.difference();LOG.sumDiff=function(message){$.gc();$.gc();var diff=$.summary.difference();if(diff.length<8){diff=' - NONE -';}if(message===undefined){message='';}message+=diff;return LOG('$.summary.difference():'+message,'v');};return LOG;}; + +var log = new LogFactory('myLog.log'); // =>; creates the new log factory - put full path where + +function getEnv(variable){ + return $.getenv(variable); +} + +function fileOpen(path){ + return app.open(new File(path)); +} + +function getLayerTypeWithName(layerName) { + var type = 'NA'; + var nameParts = layerName.split('_'); + var namePrefix = nameParts[0]; + namePrefix = namePrefix.toLowerCase(); + switch (namePrefix) { + case 'guide': + case 'tl': + case 'tr': + case 'bl': + case 'br': + type = 'GUIDE'; + break; + case 'fg': + type = 'FG'; + break; + case 'bg': + type = 'BG'; + break; + case 'obj': + default: + type = 'OBJ'; + break; + } + + return type; +} + +function getLayers() { + /** + * Get json representation of list of layers. + * Much faster this way than in DOM traversal (2s vs 45s on same file) + * + * Format of single layer info: + * id : number + * name: string + * group: boolean - true if layer is a group + * parents:array - list of ids of parent groups, useful for selection + * all children layers from parent layerSet (eg. group) + * type: string - type of layer guessed from its name + * visible:boolean - true if visible + **/ + if (documents.length == 0){ + return '[]'; + } + var ref1 = new ActionReference(); + ref1.putEnumerated(charIDToTypeID('Dcmn'), charIDToTypeID('Ordn'), + charIDToTypeID('Trgt')); + var count = executeActionGet(ref1).getInteger(charIDToTypeID('NmbL')); + + // get all layer names + var layers = []; + var layer = {}; + + var parents = []; + for (var i = count; i >= 1; i--) { + var layer = {}; + var ref2 = new ActionReference(); + ref2.putIndex(charIDToTypeID('Lyr '), i); + + var desc = executeActionGet(ref2); // Access layer index #i + var layerSection = typeIDToStringID(desc.getEnumerationValue( + stringIDToTypeID('layerSection'))); + + layer.id = desc.getInteger(stringIDToTypeID("layerID")); + layer.name = desc.getString(stringIDToTypeID("name")); + layer.color_code = typeIDToStringID(desc.getEnumerationValue(stringIDToTypeID('color'))); + layer.group = false; + layer.parents = parents.slice(); + layer.type = getLayerTypeWithName(layer.name); + layer.visible = desc.getBoolean(stringIDToTypeID("visible")); + //log(" name: " + layer.name + " groupId " + layer.groupId + + //" group " + layer.group); + if (layerSection == 'layerSectionStart') { // Group start and end + parents.push(layer.id); + layer.group = true; + } + if (layerSection == 'layerSectionEnd') { + parents.pop(); + continue; + } + layers.push(JSON.stringify(layer)); + } + try{ + var bck = activeDocument.backgroundLayer; + layer.id = bck.id; + layer.name = bck.name; + layer.group = false; + layer.parents = []; + layer.type = 'background'; + layer.visible = bck.visible; + layers.push(JSON.stringify(layer)); + }catch(e){ + // do nothing, no background layer + }; + //log("layers " + layers); + return '[' + layers + ']'; +} + +function setVisible(layer_id, visibility){ + /** + * Sets particular 'layer_id' to 'visibility' if true > show + **/ + var desc = new ActionDescriptor(); + var ref = new ActionReference(); + ref.putIdentifier(stringIDToTypeID("layer"), layer_id); + desc.putReference(stringIDToTypeID("null"), ref); + + executeAction(visibility?stringIDToTypeID("show"):stringIDToTypeID("hide"), + desc, DialogModes.NO); + +} + +function getHeadline(){ + /** + * Returns headline of current document with metadata + * + **/ + if (documents.length == 0){ + return ''; + } + var headline = app.activeDocument.info.headline; + + return headline; +} + +function isSaved(){ + return app.activeDocument.saved; +} + +function save(){ + /** Saves active document **/ + return app.activeDocument.save(); +} + +function saveAs(output_path, ext, as_copy){ + /** Exports scene to various formats + * + * Currently implemented: 'jpg', 'png', 'psd' + * + * output_path - escaped file path on local system + * ext - extension for export + * as_copy - create copy, do not overwrite + * + * */ + var saveName = output_path; + var saveOptions; + if (ext == 'jpg'){ + saveOptions = new JPEGSaveOptions(); + saveOptions.quality = 12; + saveOptions.embedColorProfile = true; + saveOptions.formatOptions = FormatOptions.PROGRESSIVE; + if(saveOptions.formatOptions == FormatOptions.PROGRESSIVE){ + saveOptions.scans = 5}; + saveOptions.matte = MatteType.NONE; + } + if (ext == 'png'){ + saveOptions = new PNGSaveOptions(); + saveOptions.interlaced = true; + saveOptions.transparency = true; + } + if (ext == 'psd'){ + saveOptions = null; + return app.activeDocument.saveAs(new File(saveName)); + } + if (ext == 'psb'){ + return savePSB(output_path); + } + + return app.activeDocument.saveAs(new File(saveName), saveOptions, as_copy); + +} + +function getActiveDocumentName(){ + /** + * Returns file name of active document + * */ + if (documents.length == 0){ + return null; + } + return app.activeDocument.name; +} + +function getActiveDocumentFullName(){ + /** + * Returns file name of active document with file path. + * activeDocument.fullName returns path in URI (eg /c/.. insted of c:/) + * */ + if (documents.length == 0){ + return null; + } + var f = new File(app.activeDocument.fullName); + var path = f.fsName; + f.close(); + return path; +} + +function imprint(payload){ + /** + * Sets headline content of current document with metadata. Stores + * information about assets created through Avalon. + * Content accessible in PS through File > File Info + * + **/ + app.activeDocument.info.headline = payload; +} + +function getSelectedLayers(doc) { + /** + * Returns json representation of currently selected layers. + * Works in three steps - 1) creates new group with selected layers + * 2) traverses this group + * 3) deletes newly created group, not neede + * Bit weird, but Adobe.. + **/ + if (doc == null){ + doc = app.activeDocument; + } + + var selLayers = []; + _grp = groupSelectedLayers(doc); + + var group = doc.activeLayer; + var layers = group.layers; + + // // group is fake at this point + // var itself_name = ''; + // if (layers){ + // itself_name = layers[0].name; + // } + + + for (var i = 0; i < layers.length; i++) { + var layer = {}; + layer.id = layers[i].id; + layer.name = layers[i].name; + long_names =_get_parents_names(group.parent, layers[i].name); + var t = layers[i].kind; + if ((typeof t !== 'undefined') && + (layers[i].kind.toString() == 'LayerKind.NORMAL')){ + layer.group = false; + }else{ + layer.group = true; + } + layer.long_name = long_names; + + selLayers.push(layer); + } + + _undo(); + + return JSON.stringify(selLayers); +}; + +function selectLayers(selectedLayers){ + /** + * Selects layers from list of ids + **/ + selectedLayers = JSON.parse(selectedLayers); + var layers = new Array(); + var id54 = charIDToTypeID( "slct" ); + var desc12 = new ActionDescriptor(); + var id55 = charIDToTypeID( "null" ); + var ref9 = new ActionReference(); + + var existing_layers = JSON.parse(getLayers()); + var existing_ids = []; + for (var y = 0; y < existing_layers.length; y++){ + existing_ids.push(existing_layers[y]["id"]); + } + for (var i = 0; i < selectedLayers.length; i++) { + // a check to see if the id stil exists + var id = selectedLayers[i]; + if(existing_ids.toString().indexOf(id)>=0){ + layers[i] = charIDToTypeID( "Lyr " ); + ref9.putIdentifier(layers[i], id); + } + } + desc12.putReference( id55, ref9 ); + var id58 = charIDToTypeID( "MkVs" ); + desc12.putBoolean( id58, false ); + executeAction( id54, desc12, DialogModes.NO ); +} + +function groupSelectedLayers(doc, name) { + /** + * Groups selected layers into new group. + * Returns json representation of Layer for server to consume + * + * Args: + * doc(activeDocument) + * name (str): new name of created group + **/ + if (doc == null){ + doc = app.activeDocument; + } + + var desc = new ActionDescriptor(); + var ref = new ActionReference(); + ref.putClass( stringIDToTypeID('layerSection') ); + desc.putReference( charIDToTypeID('null'), ref ); + var lref = new ActionReference(); + lref.putEnumerated( charIDToTypeID('Lyr '), charIDToTypeID('Ordn'), + charIDToTypeID('Trgt') ); + desc.putReference( charIDToTypeID('From'), lref); + executeAction( charIDToTypeID('Mk '), desc, DialogModes.NO ); + + var group = doc.activeLayer; + if (name){ + // Add special character to highlight group that will be published + group.name = name; + } + var layer = {}; + layer.id = group.id; + layer.name = name; // keep name clean + layer.group = true; + + layer.long_name = _get_parents_names(group, name); + + return JSON.stringify(layer); +}; + +function importSmartObject(path, name, link){ + /** + * Creates new layer with an image from 'path' + * + * path: absolute path to loaded file + * name: sets name of newly created laye + * + **/ + var desc1 = new ActionDescriptor(); + desc1.putPath( app.charIDToTypeID("null"), new File(path) ); + link = link || false; + if (link) { + desc1.putBoolean( app.charIDToTypeID('Lnkd'), true ); + } + + desc1.putEnumerated(app.charIDToTypeID("FTcs"), app.charIDToTypeID("QCSt"), + app.charIDToTypeID("Qcsa")); + var desc2 = new ActionDescriptor(); + desc2.putUnitDouble(app.charIDToTypeID("Hrzn"), + app.charIDToTypeID("#Pxl"), 0.0); + desc2.putUnitDouble(app.charIDToTypeID("Vrtc"), + app.charIDToTypeID("#Pxl"), 0.0); + + desc1.putObject(charIDToTypeID("Ofst"), charIDToTypeID("Ofst"), desc2); + executeAction(charIDToTypeID("Plc " ), desc1, DialogModes.NO); + + var docRef = app.activeDocument + var currentActivelayer = app.activeDocument.activeLayer; + if (name){ + currentActivelayer.name = name; + } + var layer = {} + layer.id = currentActivelayer.id; + layer.name = currentActivelayer.name; + return JSON.stringify(layer); +} + +function replaceSmartObjects(layer_id, path, name){ + /** + * Updates content of 'layer' with an image from 'path' + * + **/ + + var desc = new ActionDescriptor(); + var ref = new ActionReference(); + ref.putIdentifier(stringIDToTypeID("layer"), layer_id); + desc.putReference(stringIDToTypeID("null"), ref); + + desc.putPath(charIDToTypeID('null'), new File(path) ); + desc.putInteger(charIDToTypeID("PgNm"), 1); + + executeAction(stringIDToTypeID('placedLayerReplaceContents'), + desc, DialogModes.NO ); + var currentActivelayer = app.activeDocument.activeLayer; + if (name){ + currentActivelayer.name = name; + } +} + +function createGroup(name){ + /** + * Creates new group with a 'name' + * Because of asynchronous nature, only group.id is available + **/ + group = app.activeDocument.layerSets.add(); + // Add special character to highlight group that will be published + group.name = name; + + return group.id; // only id available at this time :| +} + +function deleteLayer(layer_id){ + /*** + * Deletes layer by its layer_id + * + * layer_id (int) + **/ + var d = new ActionDescriptor(); + var r = new ActionReference(); + + r.putIdentifier(stringIDToTypeID("layer"), layer_id); + d.putReference(stringIDToTypeID("null"), r); + executeAction(stringIDToTypeID("delete"), d, DialogModes.NO); +} + +function _undo() { + executeAction(charIDToTypeID("undo", undefined, DialogModes.NO)); +}; + +function savePSB(output_path){ + /*** + * Saves file as .psb to 'output_path' + * + * output_path (str) + **/ + var desc1 = new ActionDescriptor(); + var desc2 = new ActionDescriptor(); + desc2.putBoolean( stringIDToTypeID('maximizeCompatibility'), true ); + desc1.putObject( charIDToTypeID('As '), charIDToTypeID('Pht8'), desc2 ); + desc1.putPath( charIDToTypeID('In '), new File(output_path) ); + desc1.putBoolean( charIDToTypeID('LwCs'), true ); + executeAction( charIDToTypeID('save'), desc1, DialogModes.NO ); +} + +function close(){ + executeAction(stringIDToTypeID("quit"), undefined, DialogModes.NO ); +} + +function renameLayer(layer_id, new_name){ + /*** + * Renames 'layer_id' to 'new_name' + * + * Via Action (fast) + * + * Args: + * layer_id(int) + * new_name(str) + * + * output_path (str) + **/ + doc = app.activeDocument; + selectLayers('['+layer_id+']'); + + doc.activeLayer.name = new_name; +} + +function _get_parents_names(layer, itself_name){ + var long_names = [itself_name]; + while (layer.parent){ + if (layer.typename != "LayerSet"){ + break; + } + long_names.push(layer.name); + layer = layer.parent; + } + return long_names; +} + +// triggers when panel is opened, good for debugging +//log(getActiveDocumentName()); +// log.show(); +// var a = app.activeDocument.activeLayer; +// log(a); +//getSelectedLayers(); +// importSmartObject("c:/projects/test.jpg", "a aaNewLayer", true); +// log("dpc"); +// replaceSmartObjects(153, "▼Jungle_imageTest_001", "c:/projects/test_project_test_asset_TestTask_v001.png"); \ No newline at end of file diff --git a/openpype/hosts/photoshop/api/extension/host/json.js b/openpype/hosts/photoshop/api/extension/host/json.js new file mode 100644 index 00000000000..397349bbfdf --- /dev/null +++ b/openpype/hosts/photoshop/api/extension/host/json.js @@ -0,0 +1,530 @@ +// json2.js +// 2017-06-12 +// Public Domain. +// NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + +// USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO +// NOT CONTROL. + +// This file creates a global JSON object containing two methods: stringify +// and parse. This file provides the ES5 JSON capability to ES3 systems. +// If a project might run on IE8 or earlier, then this file should be included. +// This file does nothing on ES5 systems. + +// JSON.stringify(value, replacer, space) +// value any JavaScript value, usually an object or array. +// replacer an optional parameter that determines how object +// values are stringified for objects. It can be a +// function or an array of strings. +// space an optional parameter that specifies the indentation +// of nested structures. If it is omitted, the text will +// be packed without extra whitespace. If it is a number, +// it will specify the number of spaces to indent at each +// level. If it is a string (such as "\t" or " "), +// it contains the characters used to indent at each level. +// This method produces a JSON text from a JavaScript value. +// When an object value is found, if the object contains a toJSON +// method, its toJSON method will be called and the result will be +// stringified. A toJSON method does not serialize: it returns the +// value represented by the name/value pair that should be serialized, +// or undefined if nothing should be serialized. The toJSON method +// will be passed the key associated with the value, and this will be +// bound to the value. + +// For example, this would serialize Dates as ISO strings. + +// Date.prototype.toJSON = function (key) { +// function f(n) { +// // Format integers to have at least two digits. +// return (n < 10) +// ? "0" + n +// : n; +// } +// return this.getUTCFullYear() + "-" + +// f(this.getUTCMonth() + 1) + "-" + +// f(this.getUTCDate()) + "T" + +// f(this.getUTCHours()) + ":" + +// f(this.getUTCMinutes()) + ":" + +// f(this.getUTCSeconds()) + "Z"; +// }; + +// You can provide an optional replacer method. It will be passed the +// key and value of each member, with this bound to the containing +// object. The value that is returned from your method will be +// serialized. If your method returns undefined, then the member will +// be excluded from the serialization. + +// If the replacer parameter is an array of strings, then it will be +// used to select the members to be serialized. It filters the results +// such that only members with keys listed in the replacer array are +// stringified. + +// Values that do not have JSON representations, such as undefined or +// functions, will not be serialized. Such values in objects will be +// dropped; in arrays they will be replaced with null. You can use +// a replacer function to replace those with JSON values. + +// JSON.stringify(undefined) returns undefined. + +// The optional space parameter produces a stringification of the +// value that is filled with line breaks and indentation to make it +// easier to read. + +// If the space parameter is a non-empty string, then that string will +// be used for indentation. If the space parameter is a number, then +// the indentation will be that many spaces. + +// Example: + +// text = JSON.stringify(["e", {pluribus: "unum"}]); +// // text is '["e",{"pluribus":"unum"}]' + +// text = JSON.stringify(["e", {pluribus: "unum"}], null, "\t"); +// // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' + +// text = JSON.stringify([new Date()], function (key, value) { +// return this[key] instanceof Date +// ? "Date(" + this[key] + ")" +// : value; +// }); +// // text is '["Date(---current time---)"]' + +// JSON.parse(text, reviver) +// This method parses a JSON text to produce an object or array. +// It can throw a SyntaxError exception. + +// The optional reviver parameter is a function that can filter and +// transform the results. It receives each of the keys and values, +// and its return value is used instead of the original value. +// If it returns what it received, then the structure is not modified. +// If it returns undefined then the member is deleted. + +// Example: + +// // Parse the text. Values that look like ISO date strings will +// // be converted to Date objects. + +// myData = JSON.parse(text, function (key, value) { +// var a; +// if (typeof value === "string") { +// a = +// /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); +// if (a) { +// return new Date(Date.UTC( +// +a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6] +// )); +// } +// return value; +// } +// }); + +// myData = JSON.parse( +// "[\"Date(09/09/2001)\"]", +// function (key, value) { +// var d; +// if ( +// typeof value === "string" +// && value.slice(0, 5) === "Date(" +// && value.slice(-1) === ")" +// ) { +// d = new Date(value.slice(5, -1)); +// if (d) { +// return d; +// } +// } +// return value; +// } +// ); + +// This is a reference implementation. You are free to copy, modify, or +// redistribute. + +/*jslint + eval, for, this +*/ + +/*property + JSON, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, + getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, + lastIndex, length, parse, prototype, push, replace, slice, stringify, + test, toJSON, toString, valueOf +*/ + + +// Create a JSON object only if one does not already exist. We create the +// methods in a closure to avoid creating global variables. + +if (typeof JSON !== "object") { + JSON = {}; +} + +(function () { + "use strict"; + + var rx_one = /^[\],:{}\s]*$/; + var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g; + var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g; + var rx_four = /(?:^|:|,)(?:\s*\[)+/g; + var rx_escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; + var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; + + function f(n) { + // Format integers to have at least two digits. + return (n < 10) + ? "0" + n + : n; + } + + function this_value() { + return this.valueOf(); + } + + if (typeof Date.prototype.toJSON !== "function") { + + Date.prototype.toJSON = function () { + + return isFinite(this.valueOf()) + ? ( + this.getUTCFullYear() + + "-" + + f(this.getUTCMonth() + 1) + + "-" + + f(this.getUTCDate()) + + "T" + + f(this.getUTCHours()) + + ":" + + f(this.getUTCMinutes()) + + ":" + + f(this.getUTCSeconds()) + + "Z" + ) + : null; + }; + + Boolean.prototype.toJSON = this_value; + Number.prototype.toJSON = this_value; + String.prototype.toJSON = this_value; + } + + var gap; + var indent; + var meta; + var rep; + + + function quote(string) { + +// If the string contains no control characters, no quote characters, and no +// backslash characters, then we can safely slap some quotes around it. +// Otherwise we must also replace the offending characters with safe escape +// sequences. + + rx_escapable.lastIndex = 0; + return rx_escapable.test(string) + ? "\"" + string.replace(rx_escapable, function (a) { + var c = meta[a]; + return typeof c === "string" + ? c + : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4); + }) + "\"" + : "\"" + string + "\""; + } + + + function str(key, holder) { + +// Produce a string from holder[key]. + + var i; // The loop counter. + var k; // The member key. + var v; // The member value. + var length; + var mind = gap; + var partial; + var value = holder[key]; + +// If the value has a toJSON method, call it to obtain a replacement value. + + if ( + value + && typeof value === "object" + && typeof value.toJSON === "function" + ) { + value = value.toJSON(key); + } + +// If we were called with a replacer function, then call the replacer to +// obtain a replacement value. + + if (typeof rep === "function") { + value = rep.call(holder, key, value); + } + +// What happens next depends on the value's type. + + switch (typeof value) { + case "string": + return quote(value); + + case "number": + +// JSON numbers must be finite. Encode non-finite numbers as null. + + return (isFinite(value)) + ? String(value) + : "null"; + + case "boolean": + case "null": + +// If the value is a boolean or null, convert it to a string. Note: +// typeof null does not produce "null". The case is included here in +// the remote chance that this gets fixed someday. + + return String(value); + +// If the type is "object", we might be dealing with an object or an array or +// null. + + case "object": + +// Due to a specification blunder in ECMAScript, typeof null is "object", +// so watch out for that case. + + if (!value) { + return "null"; + } + +// Make an array to hold the partial results of stringifying this object value. + + gap += indent; + partial = []; + +// Is the value an array? + + if (Object.prototype.toString.apply(value) === "[object Array]") { + +// The value is an array. Stringify every element. Use null as a placeholder +// for non-JSON values. + + length = value.length; + for (i = 0; i < length; i += 1) { + partial[i] = str(i, value) || "null"; + } + +// Join all of the elements together, separated with commas, and wrap them in +// brackets. + + v = partial.length === 0 + ? "[]" + : gap + ? ( + "[\n" + + gap + + partial.join(",\n" + gap) + + "\n" + + mind + + "]" + ) + : "[" + partial.join(",") + "]"; + gap = mind; + return v; + } + +// If the replacer is an array, use it to select the members to be stringified. + + if (rep && typeof rep === "object") { + length = rep.length; + for (i = 0; i < length; i += 1) { + if (typeof rep[i] === "string") { + k = rep[i]; + v = str(k, value); + if (v) { + partial.push(quote(k) + ( + (gap) + ? ": " + : ":" + ) + v); + } + } + } + } else { + +// Otherwise, iterate through all of the keys in the object. + + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = str(k, value); + if (v) { + partial.push(quote(k) + ( + (gap) + ? ": " + : ":" + ) + v); + } + } + } + } + +// Join all of the member texts together, separated with commas, +// and wrap them in braces. + + v = partial.length === 0 + ? "{}" + : gap + ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}" + : "{" + partial.join(",") + "}"; + gap = mind; + return v; + } + } + +// If the JSON object does not yet have a stringify method, give it one. + + if (typeof JSON.stringify !== "function") { + meta = { // table of character substitutions + "\b": "\\b", + "\t": "\\t", + "\n": "\\n", + "\f": "\\f", + "\r": "\\r", + "\"": "\\\"", + "\\": "\\\\" + }; + JSON.stringify = function (value, replacer, space) { + +// The stringify method takes a value and an optional replacer, and an optional +// space parameter, and returns a JSON text. The replacer can be a function +// that can replace values, or an array of strings that will select the keys. +// A default replacer method can be provided. Use of the space parameter can +// produce text that is more easily readable. + + var i; + gap = ""; + indent = ""; + +// If the space parameter is a number, make an indent string containing that +// many spaces. + + if (typeof space === "number") { + for (i = 0; i < space; i += 1) { + indent += " "; + } + +// If the space parameter is a string, it will be used as the indent string. + + } else if (typeof space === "string") { + indent = space; + } + +// If there is a replacer, it must be a function or an array. +// Otherwise, throw an error. + + rep = replacer; + if (replacer && typeof replacer !== "function" && ( + typeof replacer !== "object" + || typeof replacer.length !== "number" + )) { + throw new Error("JSON.stringify"); + } + +// Make a fake root object containing our value under the key of "". +// Return the result of stringifying the value. + + return str("", {"": value}); + }; + } + + +// If the JSON object does not yet have a parse method, give it one. + + if (typeof JSON.parse !== "function") { + JSON.parse = function (text, reviver) { + +// The parse method takes a text and an optional reviver function, and returns +// a JavaScript value if the text is a valid JSON text. + + var j; + + function walk(holder, key) { + +// The walk method is used to recursively walk the resulting structure so +// that modifications can be made. + + var k; + var v; + var value = holder[key]; + if (value && typeof value === "object") { + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = walk(value, k); + if (v !== undefined) { + value[k] = v; + } else { + delete value[k]; + } + } + } + } + return reviver.call(holder, key, value); + } + + +// Parsing happens in four stages. In the first stage, we replace certain +// Unicode characters with escape sequences. JavaScript handles many characters +// incorrectly, either silently deleting them, or treating them as line endings. + + text = String(text); + rx_dangerous.lastIndex = 0; + if (rx_dangerous.test(text)) { + text = text.replace(rx_dangerous, function (a) { + return ( + "\\u" + + ("0000" + a.charCodeAt(0).toString(16)).slice(-4) + ); + }); + } + +// In the second stage, we run the text against regular expressions that look +// for non-JSON patterns. We are especially concerned with "()" and "new" +// because they can cause invocation, and "=" because it can cause mutation. +// But just to be safe, we want to reject all unexpected forms. + +// We split the second stage into 4 regexp operations in order to work around +// crippling inefficiencies in IE's and Safari's regexp engines. First we +// replace the JSON backslash pairs with "@" (a non-JSON character). Second, we +// replace all simple value tokens with "]" characters. Third, we delete all +// open brackets that follow a colon or comma or that begin the text. Finally, +// we look to see that the remaining characters are only whitespace or "]" or +// "," or ":" or "{" or "}". If that is so, then the text is safe for eval. + + if ( + rx_one.test( + text + .replace(rx_two, "@") + .replace(rx_three, "]") + .replace(rx_four, "") + ) + ) { + +// In the third stage we use the eval function to compile the text into a +// JavaScript structure. The "{" operator is subject to a syntactic ambiguity +// in JavaScript: it can begin a block or an object literal. We wrap the text +// in parens to eliminate the ambiguity. + + j = eval("(" + text + ")"); + +// In the optional fourth stage, we recursively walk the new structure, passing +// each name/value pair to a reviver function for possible transformation. + + return (typeof reviver === "function") + ? walk({"": j}, "") + : j; + } + +// If the text is not JSON parseable, then a SyntaxError is thrown. + + throw new SyntaxError("JSON.parse"); + }; + } +}()); \ No newline at end of file diff --git a/openpype/hosts/photoshop/api/extension/icons/avalon-logo-48.png b/openpype/hosts/photoshop/api/extension/icons/avalon-logo-48.png new file mode 100644 index 0000000000000000000000000000000000000000..33fe2a606bd1ac9d285eb0d6a90b9b14150ca3c4 GIT binary patch literal 1362 zcmV-Y1+DstP)5JpvKiH|A7SK4P~kT{6`M*9LrdQ!Ns9=$qj3(E_W@4gmP#=ht)#^0_psT^(5Px6qr0)mBB%5&-P}{Y*9ph@Pcn`!ete zwiE<#115v#ScdV2GBk!NTFTzWbF>Xip`p8%&KqcqI~Jb6tC``Vaf&07o~axnzSGF( z(ok|5&-4zgtV5rc$qke?7a8cU$D55m^%IcuOgXaxfTb~yegblyEaWJw%`Qe=-M%S@ zhOXSbt2KkcJv{&)s&PL6vC{g1Y-aKYBs(yc@x{whhk_0fK#=N=)Uup zs)>qe=dc=h3&3Gwr10?^8zc#g%1L4Xs{p!rj(uw=)9Szs&#`@sH{=+ zG+fz{pjE0VR%8l+hOX;W8`PbV32glOJ!~I2VXJkTz5Ufkuk(!F8z4>Ok_kkI+Kb}3)n06_ssJy4_*!y{BAe4)9jbBbSR!>UnLxyMT9bL9_?YdfL@K^^G6aZ)C$Qje z(NzKf2bZq2#ed1=gx1ZJQM{TNMk>CBw!wSvUjy@gS4qs1_a85GREVYsFz!+tU$`&M%7iR@HuBiw5bSa5S}|?)>G0PCUMb-Q{Pf zZt0{hEhroOCi1l=h%&q$mkBdG$MzLns~iea1>hEds{qcP5QbL){0`u*@Qfwke+13^ UGpuMiD*ylh07*qoM6N<$g1d2qT>t<8 literal 0 HcmV?d00001 diff --git a/openpype/hosts/photoshop/api/extension/index.html b/openpype/hosts/photoshop/api/extension/index.html new file mode 100644 index 00000000000..501e753c0b4 --- /dev/null +++ b/openpype/hosts/photoshop/api/extension/index.html @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + diff --git a/openpype/hosts/photoshop/api/launch_logic.py b/openpype/hosts/photoshop/api/launch_logic.py new file mode 100644 index 00000000000..36347b8ce0e --- /dev/null +++ b/openpype/hosts/photoshop/api/launch_logic.py @@ -0,0 +1,315 @@ +import os +import subprocess +import collections +import logging +import asyncio +import functools + +from wsrpc_aiohttp import ( + WebSocketRoute, + WebSocketAsync +) + +from Qt import QtCore + +from openpype.tools.utils import host_tools + +from avalon import api +from avalon.tools.webserver.app import WebServerTool + +from .ws_stub import PhotoshopServerStub + +log = logging.getLogger(__name__) +log.setLevel(logging.DEBUG) + + +class ConnectionNotEstablishedYet(Exception): + pass + + +def stub(): + """ + Convenience function to get server RPC stub to call methods directed + for host (Photoshop). + It expects already created connection, started from client. + Currently created when panel is opened (PS: Window>Extensions>Avalon) + :return: where functions could be called from + """ + ps_stub = PhotoshopServerStub() + if not ps_stub.client: + raise ConnectionNotEstablishedYet("Connection is not created yet") + + return ps_stub + + +def show_tool_by_name(tool_name): + kwargs = {} + if tool_name == "loader": + kwargs["use_context"] = True + + host_tools.show_tool_by_name(tool_name, **kwargs) + + +class ProcessLauncher(QtCore.QObject): + route_name = "Photoshop" + _main_thread_callbacks = collections.deque() + + def __init__(self, subprocess_args): + self._subprocess_args = subprocess_args + self._log = None + + super(ProcessLauncher, self).__init__() + + # Keep track if launcher was already started + self._started = False + + self._process = None + self._websocket_server = None + + start_process_timer = QtCore.QTimer() + start_process_timer.setInterval(100) + + loop_timer = QtCore.QTimer() + loop_timer.setInterval(200) + + start_process_timer.timeout.connect(self._on_start_process_timer) + loop_timer.timeout.connect(self._on_loop_timer) + + self._start_process_timer = start_process_timer + self._loop_timer = loop_timer + + @property + def log(self): + if self._log is None: + from openpype.api import Logger + + self._log = Logger.get_logger("{}-launcher".format( + self.route_name)) + return self._log + + @property + def websocket_server_is_running(self): + if self._websocket_server is not None: + return self._websocket_server.is_running + return False + + @property + def is_process_running(self): + if self._process is not None: + return self._process.poll() is None + return False + + @property + def is_host_connected(self): + """Returns True if connected, False if app is not running at all.""" + if not self.is_process_running: + return False + + try: + + _stub = stub() + if _stub: + return True + except Exception: + pass + + return None + + @classmethod + def execute_in_main_thread(cls, callback): + cls._main_thread_callbacks.append(callback) + + def start(self): + if self._started: + return + self.log.info("Started launch logic of AfterEffects") + self._started = True + self._start_process_timer.start() + + def exit(self): + """ Exit whole application. """ + if self._start_process_timer.isActive(): + self._start_process_timer.stop() + if self._loop_timer.isActive(): + self._loop_timer.stop() + + if self._websocket_server is not None: + self._websocket_server.stop() + + if self._process: + self._process.kill() + self._process.wait() + + QtCore.QCoreApplication.exit() + + def _on_loop_timer(self): + # TODO find better way and catch errors + # Run only callbacks that are in queue at the moment + cls = self.__class__ + for _ in range(len(cls._main_thread_callbacks)): + if cls._main_thread_callbacks: + callback = cls._main_thread_callbacks.popleft() + callback() + + if not self.is_process_running: + self.log.info("Host process is not running. Closing") + self.exit() + + elif not self.websocket_server_is_running: + self.log.info("Websocket server is not running. Closing") + self.exit() + + def _on_start_process_timer(self): + # TODO add try except validations for each part in this method + # Start server as first thing + if self._websocket_server is None: + self._init_server() + return + + # TODO add waiting time + # Wait for webserver + if not self.websocket_server_is_running: + return + + # Start application process + if self._process is None: + self._start_process() + self.log.info("Waiting for host to connect") + return + + # TODO add waiting time + # Wait until host is connected + if self.is_host_connected: + self._start_process_timer.stop() + self._loop_timer.start() + elif ( + not self.is_process_running + or not self.websocket_server_is_running + ): + self.exit() + + def _init_server(self): + if self._websocket_server is not None: + return + + self.log.debug( + "Initialization of websocket server for host communication" + ) + + self._websocket_server = websocket_server = WebServerTool() + if websocket_server.port_occupied( + websocket_server.host_name, + websocket_server.port + ): + self.log.info( + "Server already running, sending actual context and exit." + ) + asyncio.run(websocket_server.send_context_change(self.route_name)) + self.exit() + return + + # Add Websocket route + websocket_server.add_route("*", "/ws/", WebSocketAsync) + # Add after effects route to websocket handler + + print("Adding {} route".format(self.route_name)) + WebSocketAsync.add_route( + self.route_name, PhotoshopRoute + ) + self.log.info("Starting websocket server for host communication") + websocket_server.start_server() + + def _start_process(self): + if self._process is not None: + return + self.log.info("Starting host process") + try: + self._process = subprocess.Popen( + self._subprocess_args, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL + ) + except Exception: + self.log.info("exce", exc_info=True) + self.exit() + + +class PhotoshopRoute(WebSocketRoute): + """ + One route, mimicking external application (like Harmony, etc). + All functions could be called from client. + 'do_notify' function calls function on the client - mimicking + notification after long running job on the server or similar + """ + instance = None + + def init(self, **kwargs): + # Python __init__ must be return "self". + # This method might return anything. + log.debug("someone called Photoshop route") + self.instance = self + return kwargs + + # server functions + async def ping(self): + log.debug("someone called Photoshop route ping") + + # This method calls function on the client side + # client functions + async def set_context(self, project, asset, task): + """ + Sets 'project' and 'asset' to envs, eg. setting context + + Args: + project (str) + asset (str) + """ + log.info("Setting context change") + log.info("project {} asset {} ".format(project, asset)) + if project: + api.Session["AVALON_PROJECT"] = project + os.environ["AVALON_PROJECT"] = project + if asset: + api.Session["AVALON_ASSET"] = asset + os.environ["AVALON_ASSET"] = asset + if task: + api.Session["AVALON_TASK"] = task + os.environ["AVALON_TASK"] = task + + async def read(self): + log.debug("photoshop.read client calls server server calls " + "photoshop client") + return await self.socket.call('photoshop.read') + + # panel routes for tools + async def creator_route(self): + self._tool_route("creator") + + async def workfiles_route(self): + self._tool_route("workfiles") + + async def loader_route(self): + self._tool_route("loader") + + async def publish_route(self): + self._tool_route("publish") + + async def sceneinventory_route(self): + self._tool_route("sceneinventory") + + async def subsetmanager_route(self): + self._tool_route("subsetmanager") + + async def experimental_tools_route(self): + self._tool_route("experimental_tools") + + def _tool_route(self, _tool_name): + """The address accessed when clicking on the buttons.""" + + partial_method = functools.partial(show_tool_by_name, + _tool_name) + + ProcessLauncher.execute_in_main_thread(partial_method) + + # Required return statement. + return "nothing" diff --git a/openpype/hosts/photoshop/api/lib.py b/openpype/hosts/photoshop/api/lib.py new file mode 100644 index 00000000000..bc1fb36cf38 --- /dev/null +++ b/openpype/hosts/photoshop/api/lib.py @@ -0,0 +1,76 @@ +import os +import sys +import contextlib +import logging +import traceback + +from Qt import QtWidgets + +from openpype.tools.utils import host_tools + +from openpype.lib.remote_publish import headless_publish + +from .launch_logic import ProcessLauncher, stub + +log = logging.getLogger(__name__) +log.setLevel(logging.DEBUG) + + +def safe_excepthook(*args): + traceback.print_exception(*args) + + +def main(*subprocess_args): + from avalon import api, photoshop + + api.install(photoshop) + sys.excepthook = safe_excepthook + + # coloring in ConsoleTrayApp + os.environ["OPENPYPE_LOG_NO_COLORS"] = "False" + app = QtWidgets.QApplication([]) + app.setQuitOnLastWindowClosed(False) + + launcher = ProcessLauncher(subprocess_args) + launcher.start() + + if os.environ.get("HEADLESS_PUBLISH"): + launcher.execute_in_main_thread(lambda: headless_publish( + log, + "ClosePS", + os.environ.get("IS_TEST"))) + elif os.environ.get("AVALON_PHOTOSHOP_WORKFILES_ON_LAUNCH", True): + save = False + if os.getenv("WORKFILES_SAVE_AS"): + save = True + + launcher.execute_in_main_thread( + lambda: host_tools.show_workfiles(save=save) + ) + + sys.exit(app.exec_()) + + +@contextlib.contextmanager +def maintained_selection(): + """Maintain selection during context.""" + selection = stub().get_selected_layers() + try: + yield selection + finally: + stub().select_layers(selection) + + +@contextlib.contextmanager +def maintained_visibility(): + """Maintain visibility during context.""" + visibility = {} + layers = stub().get_layers() + for layer in layers: + visibility[layer.id] = layer.visible + try: + yield + finally: + for layer in layers: + stub().set_visible(layer.id, visibility[layer.id]) + pass diff --git a/openpype/hosts/photoshop/api/panel.PNG b/openpype/hosts/photoshop/api/panel.PNG new file mode 100644 index 0000000000000000000000000000000000000000..be5db3b8df08aa426b92de61eacc9afd00e93d45 GIT binary patch literal 8756 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 literal 0 HcmV?d00001 diff --git a/openpype/hosts/photoshop/api/pipeline.py b/openpype/hosts/photoshop/api/pipeline.py new file mode 100644 index 00000000000..ade144e6d43 --- /dev/null +++ b/openpype/hosts/photoshop/api/pipeline.py @@ -0,0 +1,199 @@ +from .. import api, pipeline +from . import lib +from ..vendor import Qt + +import pyblish.api + + +def install(): + """Install Photoshop-specific functionality of avalon-core. + + This function is called automatically on calling `api.install(photoshop)`. + """ + print("Installing Avalon Photoshop...") + pyblish.api.register_host("photoshop") + + +def ls(): + """Yields containers from active Photoshop document + + This is the host-equivalent of api.ls(), but instead of listing + assets on disk, it lists assets already loaded in Photoshop; once loaded + they are called 'containers' + + Yields: + dict: container + + """ + try: + stub = lib.stub() # only after Photoshop is up + except lib.ConnectionNotEstablishedYet: + print("Not connected yet, ignoring") + return + + if not stub.get_active_document_name(): + return + + layers_meta = stub.get_layers_metadata() # minimalize calls to PS + for layer in stub.get_layers(): + data = stub.read(layer, layers_meta) + + # Skip non-tagged layers. + if not data: + continue + + # Filter to only containers. + if "container" not in data["id"]: + continue + + # Append transient data + data["objectName"] = layer.name.replace(stub.LOADED_ICON, '') + data["layer"] = layer + + yield data + + +def list_instances(): + """ + List all created instances from current workfile which + will be published. + + Pulls from File > File Info + + For SubsetManager + + Returns: + (list) of dictionaries matching instances format + """ + stub = _get_stub() + + if not stub: + return [] + + instances = [] + layers_meta = stub.get_layers_metadata() + if layers_meta: + for key, instance in layers_meta.items(): + if instance.get("schema") and \ + "container" in instance.get("schema"): + continue + + instance['uuid'] = key + instances.append(instance) + + return instances + + +def remove_instance(instance): + """ + Remove instance from current workfile metadata. + + Updates metadata of current file in File > File Info and removes + icon highlight on group layer. + + For SubsetManager + + Args: + instance (dict): instance representation from subsetmanager model + """ + stub = _get_stub() + + if not stub: + return + + stub.remove_instance(instance.get("uuid")) + layer = stub.get_layer(instance.get("uuid")) + if layer: + stub.rename_layer(instance.get("uuid"), + layer.name.replace(stub.PUBLISH_ICON, '')) + + +def _get_stub(): + """ + Handle pulling stub from PS to run operations on host + Returns: + (PhotoshopServerStub) or None + """ + try: + stub = lib.stub() # only after Photoshop is up + except lib.ConnectionNotEstablishedYet: + print("Not connected yet, ignoring") + return + + if not stub.get_active_document_name(): + return + + return stub + + +class Creator(api.Creator): + """Creator plugin to create instances in Photoshop + + A LayerSet is created to support any number of layers in an instance. If + the selection is used, these layers will be added to the LayerSet. + """ + + def process(self): + # Photoshop can have multiple LayerSets with the same name, which does + # not work with Avalon. + msg = "Instance with name \"{}\" already exists.".format(self.name) + stub = lib.stub() # only after Photoshop is up + for layer in stub.get_layers(): + if self.name.lower() == layer.Name.lower(): + msg = Qt.QtWidgets.QMessageBox() + msg.setIcon(Qt.QtWidgets.QMessageBox.Warning) + msg.setText(msg) + msg.exec_() + return False + + # Store selection because adding a group will change selection. + with lib.maintained_selection(): + + # Add selection to group. + if (self.options or {}).get("useSelection"): + group = stub.group_selected_layers(self.name) + else: + group = stub.create_group(self.name) + + stub.imprint(group, self.data) + + return group + + +def containerise(name, + namespace, + layer, + context, + loader=None, + suffix="_CON"): + """Imprint layer with metadata + + Containerisation enables a tracking of version, author and origin + for loaded assets. + + Arguments: + name (str): Name of resulting assembly + namespace (str): Namespace under which to host container + layer (PSItem): Layer to containerise + context (dict): Asset information + loader (str, optional): Name of loader used to produce this container. + suffix (str, optional): Suffix of container, defaults to `_CON`. + + Returns: + container (str): Name of container assembly + """ + layer.name = name + suffix + + data = { + "schema": "openpype:container-2.0", + "id": pipeline.AVALON_CONTAINER_ID, + "name": name, + "namespace": namespace, + "loader": str(loader), + "representation": str(context["representation"]["_id"]), + "members": [str(layer.id)] + } + stub = lib.stub() + stub.imprint(layer, data) + + return layer diff --git a/openpype/hosts/photoshop/api/workio.py b/openpype/hosts/photoshop/api/workio.py new file mode 100644 index 00000000000..ddcd351b381 --- /dev/null +++ b/openpype/hosts/photoshop/api/workio.py @@ -0,0 +1,50 @@ +"""Host API required Work Files tool""" +import os + +from . import lib +from avalon import api + + +def _active_document(): + document_name = lib.stub().get_active_document_name() + if not document_name: + return None + + return document_name + + +def file_extensions(): + return api.HOST_WORKFILE_EXTENSIONS["photoshop"] + + +def has_unsaved_changes(): + if _active_document(): + return not lib.stub().is_saved() + + return False + + +def save_file(filepath): + _, ext = os.path.splitext(filepath) + lib.stub().saveAs(filepath, ext[1:], True) + + +def open_file(filepath): + lib.stub().open(filepath) + + return True + + +def current_file(): + try: + full_name = lib.stub().get_active_document_full_name() + if full_name and full_name != "null": + return os.path.normpath(full_name).replace("\\", "/") + except Exception: + pass + + return None + + +def work_root(session): + return os.path.normpath(session["AVALON_WORKDIR"]).replace("\\", "/") diff --git a/openpype/hosts/photoshop/api/ws_stub.py b/openpype/hosts/photoshop/api/ws_stub.py new file mode 100644 index 00000000000..f7bd03cdab1 --- /dev/null +++ b/openpype/hosts/photoshop/api/ws_stub.py @@ -0,0 +1,470 @@ +""" + Stub handling connection from server to client. + Used anywhere solution is calling client methods. +""" +import json +import sys +from wsrpc_aiohttp import WebSocketAsync +import attr + +from avalon.tools.webserver.app import WebServerTool + + +@attr.s +class PSItem(object): + """ + Object denoting layer or group item in PS. Each item is created in + PS by any Loader, but contains same fields, which are being used + in later processing. + """ + # metadata + id = attr.ib() # id created by AE, could be used for querying + name = attr.ib() # name of item + group = attr.ib(default=None) # item type (footage, folder, comp) + parents = attr.ib(factory=list) + visible = attr.ib(default=True) + type = attr.ib(default=None) + # all imported elements, single for + members = attr.ib(factory=list) + long_name = attr.ib(default=None) + color_code = attr.ib(default=None) # color code of layer + + +class PhotoshopServerStub: + """ + Stub for calling function on client (Photoshop js) side. + Expects that client is already connected (started when avalon menu + is opened). + 'self.websocketserver.call' is used as async wrapper + """ + PUBLISH_ICON = '\u2117 ' + LOADED_ICON = '\u25bc' + + def __init__(self): + self.websocketserver = WebServerTool.get_instance() + self.client = self.get_client() + + @staticmethod + def get_client(): + """ + Return first connected client to WebSocket + TODO implement selection by Route + :return: client + """ + clients = WebSocketAsync.get_clients() + client = None + if len(clients) > 0: + key = list(clients.keys())[0] + client = clients.get(key) + + return client + + def open(self, path): + """ + Open file located at 'path' (local). + Args: + path(string): file path locally + Returns: None + """ + self.websocketserver.call(self.client.call + ('Photoshop.open', path=path) + ) + + def read(self, layer, layers_meta=None): + """ + Parses layer metadata from Headline field of active document + Args: + layer: (PSItem) + layers_meta: full list from Headline (for performance in loops) + Returns: + """ + if layers_meta is None: + layers_meta = self.get_layers_metadata() + + return layers_meta.get(str(layer.id)) + + def imprint(self, layer, data, all_layers=None, layers_meta=None): + """ + Save layer metadata to Headline field of active document + + Stores metadata in format: + [{ + "active":true, + "subset":"imageBG", + "family":"image", + "id":"pyblish.avalon.instance", + "asset":"Town", + "uuid": "8" + }] - for created instances + OR + [{ + "schema": "openpype:container-2.0", + "id": "pyblish.avalon.instance", + "name": "imageMG", + "namespace": "Jungle_imageMG_001", + "loader": "ImageLoader", + "representation": "5fbfc0ee30a946093c6ff18a", + "members": [ + "40" + ] + }] - for loaded instances + + Args: + layer (PSItem): + data(string): json representation for single layer + all_layers (list of PSItem): for performance, could be + injected for usage in loop, if not, single call will be + triggered + layers_meta(string): json representation from Headline + (for performance - provide only if imprint is in + loop - value should be same) + Returns: None + """ + if not layers_meta: + layers_meta = self.get_layers_metadata() + + # json.dumps writes integer values in a dictionary to string, so + # anticipating it here. + if str(layer.id) in layers_meta and layers_meta[str(layer.id)]: + if data: + layers_meta[str(layer.id)].update(data) + else: + layers_meta.pop(str(layer.id)) + else: + layers_meta[str(layer.id)] = data + + # Ensure only valid ids are stored. + if not all_layers: + all_layers = self.get_layers() + layer_ids = [layer.id for layer in all_layers] + cleaned_data = [] + + for id in layers_meta: + if int(id) in layer_ids: + cleaned_data.append(layers_meta[id]) + + payload = json.dumps(cleaned_data, indent=4) + + self.websocketserver.call(self.client.call + ('Photoshop.imprint', payload=payload) + ) + + def get_layers(self): + """ + Returns JSON document with all(?) layers in active document. + + Returns: + Format of tuple: { 'id':'123', + 'name': 'My Layer 1', + 'type': 'GUIDE'|'FG'|'BG'|'OBJ' + 'visible': 'true'|'false' + """ + res = self.websocketserver.call(self.client.call + ('Photoshop.get_layers')) + + return self._to_records(res) + + def get_layer(self, layer_id): + """ + Returns PSItem for specific 'layer_id' or None if not found + Args: + layer_id (string): unique layer id, stored in 'uuid' field + + Returns: + (PSItem) or None + """ + layers = self.get_layers() + for layer in layers: + if str(layer.id) == str(layer_id): + return layer + + def get_layers_in_layers(self, layers): + """ + Return all layers that belong to layers (might be groups). + Args: + layers : + Returns: + """ + all_layers = self.get_layers() + ret = [] + parent_ids = set([lay.id for lay in layers]) + + for layer in all_layers: + parents = set(layer.parents) + if len(parent_ids & parents) > 0: + ret.append(layer) + if layer.id in parent_ids: + ret.append(layer) + + return ret + + def create_group(self, name): + """ + Create new group (eg. LayerSet) + Returns: + """ + enhanced_name = self.PUBLISH_ICON + name + ret = self.websocketserver.call(self.client.call + ('Photoshop.create_group', + name=enhanced_name)) + # create group on PS is asynchronous, returns only id + return PSItem(id=ret, name=name, group=True) + + def group_selected_layers(self, name): + """ + Group selected layers into new LayerSet (eg. group) + Returns: (Layer) + """ + enhanced_name = self.PUBLISH_ICON + name + res = self.websocketserver.call(self.client.call + ('Photoshop.group_selected_layers', + name=enhanced_name) + ) + res = self._to_records(res) + if res: + rec = res.pop() + rec.name = rec.name.replace(self.PUBLISH_ICON, '') + return rec + raise ValueError("No group record returned") + + def get_selected_layers(self): + """ + Get a list of actually selected layers + Returns: + """ + res = self.websocketserver.call(self.client.call + ('Photoshop.get_selected_layers')) + return self._to_records(res) + + def select_layers(self, layers): + """ + Selects specified layers in Photoshop by its ids + Args: + layers: + Returns: None + """ + layers_id = [str(lay.id) for lay in layers] + self.websocketserver.call(self.client.call + ('Photoshop.select_layers', + layers=json.dumps(layers_id)) + ) + + def get_active_document_full_name(self): + """ + Returns full name with path of active document via ws call + Returns(string): full path with name + """ + res = self.websocketserver.call( + self.client.call('Photoshop.get_active_document_full_name')) + + return res + + def get_active_document_name(self): + """ + Returns just a name of active document via ws call + Returns(string): file name + """ + res = self.websocketserver.call(self.client.call + ('Photoshop.get_active_document_name')) + + return res + + def is_saved(self): + """ + Returns true if no changes in active document + Returns: + """ + return self.websocketserver.call(self.client.call + ('Photoshop.is_saved')) + + def save(self): + """ + Saves active document + Returns: None + """ + self.websocketserver.call(self.client.call + ('Photoshop.save')) + + def saveAs(self, image_path, ext, as_copy): + """ + Saves active document to psd (copy) or png or jpg + Args: + image_path(string): full local path + ext: + as_copy: + Returns: None + """ + self.websocketserver.call(self.client.call + ('Photoshop.saveAs', + image_path=image_path, + ext=ext, + as_copy=as_copy)) + + def set_visible(self, layer_id, visibility): + """ + Set layer with 'layer_id' to 'visibility' + Args: + layer_id: + visibility: + Returns: None + """ + self.websocketserver.call(self.client.call + ('Photoshop.set_visible', + layer_id=layer_id, + visibility=visibility)) + + def get_layers_metadata(self): + """ + Reads layers metadata from Headline from active document in PS. + (Headline accessible by File > File Info) + + Returns: + (string): - json documents + example: + {"8":{"active":true,"subset":"imageBG", + "family":"image","id":"pyblish.avalon.instance", + "asset":"Town"}} + 8 is layer(group) id - used for deletion, update etc. + """ + layers_data = {} + res = self.websocketserver.call(self.client.call('Photoshop.read')) + try: + layers_data = json.loads(res) + except json.decoder.JSONDecodeError: + pass + # format of metadata changed from {} to [] because of standardization + # keep current implementation logic as its working + if not isinstance(layers_data, dict): + temp_layers_meta = {} + for layer_meta in layers_data: + layer_id = layer_meta.get("uuid") or \ + (layer_meta.get("members")[0]) + temp_layers_meta[layer_id] = layer_meta + layers_data = temp_layers_meta + else: + # legacy version of metadata + for layer_id, layer_meta in layers_data.items(): + if layer_meta.get("schema") != "openpype:container-2.0": + layer_meta["uuid"] = str(layer_id) + else: + layer_meta["members"] = [str(layer_id)] + + return layers_data + + def import_smart_object(self, path, layer_name, as_reference=False): + """ + Import the file at `path` as a smart object to active document. + + Args: + path (str): File path to import. + layer_name (str): Unique layer name to differentiate how many times + same smart object was loaded + as_reference (bool): pull in content or reference + """ + enhanced_name = self.LOADED_ICON + layer_name + res = self.websocketserver.call(self.client.call + ('Photoshop.import_smart_object', + path=path, name=enhanced_name, + as_reference=as_reference + )) + rec = self._to_records(res).pop() + if rec: + rec.name = rec.name.replace(self.LOADED_ICON, '') + return rec + + def replace_smart_object(self, layer, path, layer_name): + """ + Replace the smart object `layer` with file at `path` + layer_name (str): Unique layer name to differentiate how many times + same smart object was loaded + + Args: + layer (PSItem): + path (str): File to import. + """ + enhanced_name = self.LOADED_ICON + layer_name + self.websocketserver.call(self.client.call + ('Photoshop.replace_smart_object', + layer_id=layer.id, + path=path, name=enhanced_name)) + + def delete_layer(self, layer_id): + """ + Deletes specific layer by it's id. + Args: + layer_id (int): id of layer to delete + """ + self.websocketserver.call(self.client.call + ('Photoshop.delete_layer', + layer_id=layer_id)) + + def rename_layer(self, layer_id, name): + """ + Renames specific layer by it's id. + Args: + layer_id (int): id of layer to delete + name (str): new name + """ + self.websocketserver.call(self.client.call + ('Photoshop.rename_layer', + layer_id=layer_id, + name=name)) + + def remove_instance(self, instance_id): + cleaned_data = {} + + for key, instance in self.get_layers_metadata().items(): + if key != instance_id: + cleaned_data[key] = instance + + payload = json.dumps(cleaned_data, indent=4) + + self.websocketserver.call(self.client.call + ('Photoshop.imprint', payload=payload) + ) + + def get_extension_version(self): + """Returns version number of installed extension.""" + return self.websocketserver.call(self.client.call + ('Photoshop.get_extension_version')) + + def close(self): + """Shutting down PS and process too. + + For webpublishing only. + """ + # TODO change client.call to method with checks for client + self.websocketserver.call(self.client.call('Photoshop.close')) + + def _to_records(self, res): + """ + Converts string json representation into list of PSItem for + dot notation access to work. + Args: + res (string): valid json + Returns: + + """ + try: + layers_data = json.loads(res) + except json.decoder.JSONDecodeError: + raise ValueError("Received broken JSON {}".format(res)) + ret = [] + + # convert to AEItem to use dot donation + if isinstance(layers_data, dict): + layers_data = [layers_data] + for d in layers_data: + # currently implemented and expected fields + item = PSItem(d.get('id'), + d.get('name'), + d.get('group'), + d.get('parents'), + d.get('visible'), + d.get('type'), + d.get('members'), + d.get('long_name'), + d.get("color_code")) + + ret.append(item) + return ret From 3ca400949134fc604744086ae74c8b16bd0d8757 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 10 Jan 2022 23:00:53 +0100 Subject: [PATCH 442/492] removed empty hooks --- openpype/hosts/photoshop/hooks/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 openpype/hosts/photoshop/hooks/__init__.py diff --git a/openpype/hosts/photoshop/hooks/__init__.py b/openpype/hosts/photoshop/hooks/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 From c09d832c73db12455ec56d1429510f5c43772d56 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 10 Jan 2022 23:04:35 +0100 Subject: [PATCH 443/492] use openpype logger --- openpype/hosts/photoshop/api/launch_logic.py | 13 +++++-------- openpype/hosts/photoshop/api/lib.py | 5 ++--- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/openpype/hosts/photoshop/api/launch_logic.py b/openpype/hosts/photoshop/api/launch_logic.py index 36347b8ce0e..8e0d00636ab 100644 --- a/openpype/hosts/photoshop/api/launch_logic.py +++ b/openpype/hosts/photoshop/api/launch_logic.py @@ -1,7 +1,6 @@ import os import subprocess import collections -import logging import asyncio import functools @@ -12,6 +11,7 @@ from Qt import QtCore +from openpype.api import Logger from openpype.tools.utils import host_tools from avalon import api @@ -19,8 +19,7 @@ from .ws_stub import PhotoshopServerStub -log = logging.getLogger(__name__) -log.setLevel(logging.DEBUG) +log = Logger.get_logger(__name__) class ConnectionNotEstablishedYet(Exception): @@ -81,10 +80,9 @@ def __init__(self, subprocess_args): @property def log(self): if self._log is None: - from openpype.api import Logger - - self._log = Logger.get_logger("{}-launcher".format( - self.route_name)) + self._log = Logger.get_logger( + "{}-launcher".format(self.route_name) + ) return self._log @property @@ -106,7 +104,6 @@ def is_host_connected(self): return False try: - _stub = stub() if _stub: return True diff --git a/openpype/hosts/photoshop/api/lib.py b/openpype/hosts/photoshop/api/lib.py index bc1fb36cf38..0938febf435 100644 --- a/openpype/hosts/photoshop/api/lib.py +++ b/openpype/hosts/photoshop/api/lib.py @@ -1,19 +1,18 @@ import os import sys import contextlib -import logging import traceback from Qt import QtWidgets from openpype.tools.utils import host_tools +from openpype.api import Logger from openpype.lib.remote_publish import headless_publish from .launch_logic import ProcessLauncher, stub -log = logging.getLogger(__name__) -log.setLevel(logging.DEBUG) +log = Logger.get_logger(__name__) def safe_excepthook(*args): From 1a623d6ee2a17246a881a2d214e9191e7a54aeab Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 10 Jan 2022 23:05:05 +0100 Subject: [PATCH 444/492] formatting changes --- openpype/hosts/photoshop/api/workio.py | 5 +- openpype/hosts/photoshop/api/ws_stub.py | 351 +++++++++++++----------- 2 files changed, 191 insertions(+), 165 deletions(-) diff --git a/openpype/hosts/photoshop/api/workio.py b/openpype/hosts/photoshop/api/workio.py index ddcd351b381..0bf3ed2bd94 100644 --- a/openpype/hosts/photoshop/api/workio.py +++ b/openpype/hosts/photoshop/api/workio.py @@ -1,8 +1,9 @@ """Host API required Work Files tool""" import os +import avalon.api + from . import lib -from avalon import api def _active_document(): @@ -14,7 +15,7 @@ def _active_document(): def file_extensions(): - return api.HOST_WORKFILE_EXTENSIONS["photoshop"] + return avalon.api.HOST_WORKFILE_EXTENSIONS["photoshop"] def has_unsaved_changes(): diff --git a/openpype/hosts/photoshop/api/ws_stub.py b/openpype/hosts/photoshop/api/ws_stub.py index f7bd03cdab1..b8f66332c64 100644 --- a/openpype/hosts/photoshop/api/ws_stub.py +++ b/openpype/hosts/photoshop/api/ws_stub.py @@ -2,10 +2,10 @@ Stub handling connection from server to client. Used anywhere solution is calling client methods. """ -import json import sys -from wsrpc_aiohttp import WebSocketAsync +import json import attr +from wsrpc_aiohttp import WebSocketAsync from avalon.tools.webserver.app import WebServerTool @@ -60,19 +60,19 @@ def get_client(): return client def open(self, path): - """ - Open file located at 'path' (local). + """Open file located at 'path' (local). + Args: path(string): file path locally Returns: None """ - self.websocketserver.call(self.client.call - ('Photoshop.open', path=path) - ) + self.websocketserver.call( + self.client.call('Photoshop.open', path=path) + ) def read(self, layer, layers_meta=None): - """ - Parses layer metadata from Headline field of active document + """Parses layer metadata from Headline field of active document. + Args: layer: (PSItem) layers_meta: full list from Headline (for performance in loops) @@ -84,30 +84,29 @@ def read(self, layer, layers_meta=None): return layers_meta.get(str(layer.id)) def imprint(self, layer, data, all_layers=None, layers_meta=None): - """ - Save layer metadata to Headline field of active document - - Stores metadata in format: - [{ - "active":true, - "subset":"imageBG", - "family":"image", - "id":"pyblish.avalon.instance", - "asset":"Town", - "uuid": "8" - }] - for created instances - OR - [{ - "schema": "openpype:container-2.0", - "id": "pyblish.avalon.instance", - "name": "imageMG", - "namespace": "Jungle_imageMG_001", - "loader": "ImageLoader", - "representation": "5fbfc0ee30a946093c6ff18a", - "members": [ - "40" - ] - }] - for loaded instances + """Save layer metadata to Headline field of active document + + Stores metadata in format: + [{ + "active":true, + "subset":"imageBG", + "family":"image", + "id":"pyblish.avalon.instance", + "asset":"Town", + "uuid": "8" + }] - for created instances + OR + [{ + "schema": "openpype:container-2.0", + "id": "pyblish.avalon.instance", + "name": "imageMG", + "namespace": "Jungle_imageMG_001", + "loader": "ImageLoader", + "representation": "5fbfc0ee30a946093c6ff18a", + "members": [ + "40" + ] + }] - for loaded instances Args: layer (PSItem): @@ -139,19 +138,18 @@ def imprint(self, layer, data, all_layers=None, layers_meta=None): layer_ids = [layer.id for layer in all_layers] cleaned_data = [] - for id in layers_meta: - if int(id) in layer_ids: - cleaned_data.append(layers_meta[id]) + for layer_id in layers_meta: + if int(layer_id) in layer_ids: + cleaned_data.append(layers_meta[layer_id]) payload = json.dumps(cleaned_data, indent=4) - self.websocketserver.call(self.client.call - ('Photoshop.imprint', payload=payload) - ) + self.websocketserver.call( + self.client.call('Photoshop.imprint', payload=payload) + ) def get_layers(self): - """ - Returns JSON document with all(?) layers in active document. + """Returns JSON document with all(?) layers in active document. Returns: Format of tuple: { 'id':'123', @@ -159,8 +157,9 @@ def get_layers(self): 'type': 'GUIDE'|'FG'|'BG'|'OBJ' 'visible': 'true'|'false' """ - res = self.websocketserver.call(self.client.call - ('Photoshop.get_layers')) + res = self.websocketserver.call( + self.client.call('Photoshop.get_layers') + ) return self._to_records(res) @@ -179,11 +178,13 @@ def get_layer(self, layer_id): return layer def get_layers_in_layers(self, layers): - """ - Return all layers that belong to layers (might be groups). + """Return all layers that belong to layers (might be groups). + Args: layers : - Returns: + + Returns: + """ all_layers = self.get_layers() ret = [] @@ -199,27 +200,30 @@ def get_layers_in_layers(self, layers): return ret def create_group(self, name): - """ - Create new group (eg. LayerSet) - Returns: + """Create new group (eg. LayerSet) + + Returns: + """ enhanced_name = self.PUBLISH_ICON + name - ret = self.websocketserver.call(self.client.call - ('Photoshop.create_group', - name=enhanced_name)) + ret = self.websocketserver.call( + self.client.call('Photoshop.create_group', name=enhanced_name) + ) # create group on PS is asynchronous, returns only id return PSItem(id=ret, name=name, group=True) def group_selected_layers(self, name): - """ - Group selected layers into new LayerSet (eg. group) - Returns: (Layer) + """Group selected layers into new LayerSet (eg. group) + + Returns: + (Layer) """ enhanced_name = self.PUBLISH_ICON + name - res = self.websocketserver.call(self.client.call - ('Photoshop.group_selected_layers', - name=enhanced_name) - ) + res = self.websocketserver.call( + self.client.call( + 'Photoshop.group_selected_layers', name=enhanced_name + ) + ) res = self._to_records(res) if res: rec = res.pop() @@ -228,103 +232,112 @@ def group_selected_layers(self, name): raise ValueError("No group record returned") def get_selected_layers(self): - """ - Get a list of actually selected layers + """Get a list of actually selected layers. + Returns: """ - res = self.websocketserver.call(self.client.call - ('Photoshop.get_selected_layers')) + res = self.websocketserver.call( + self.client.call('Photoshop.get_selected_layers') + ) return self._to_records(res) def select_layers(self, layers): - """ - Selects specified layers in Photoshop by its ids + """Selects specified layers in Photoshop by its ids. + Args: layers: - Returns: None """ layers_id = [str(lay.id) for lay in layers] - self.websocketserver.call(self.client.call - ('Photoshop.select_layers', - layers=json.dumps(layers_id)) - ) + self.websocketserver.call( + self.client.call( + 'Photoshop.select_layers', + layers=json.dumps(layers_id) + ) + ) def get_active_document_full_name(self): - """ - Returns full name with path of active document via ws call - Returns(string): full path with name + """Returns full name with path of active document via ws call + + Returns(string): + full path with name """ res = self.websocketserver.call( - self.client.call('Photoshop.get_active_document_full_name')) + self.client.call('Photoshop.get_active_document_full_name') + ) return res def get_active_document_name(self): - """ - Returns just a name of active document via ws call - Returns(string): file name - """ - res = self.websocketserver.call(self.client.call - ('Photoshop.get_active_document_name')) + """Returns just a name of active document via ws call - return res + Returns(string): + file name + """ + return self.websocketserver.call( + self.client.call('Photoshop.get_active_document_name') + ) def is_saved(self): + """Returns true if no changes in active document + + Returns: + """ - Returns true if no changes in active document - Returns: - """ - return self.websocketserver.call(self.client.call - ('Photoshop.is_saved')) + return self.websocketserver.call( + self.client.call('Photoshop.is_saved') + ) def save(self): - """ - Saves active document - Returns: None - """ - self.websocketserver.call(self.client.call - ('Photoshop.save')) + """Saves active document""" + self.websocketserver.call( + self.client.call('Photoshop.save') + ) def saveAs(self, image_path, ext, as_copy): - """ - Saves active document to psd (copy) or png or jpg + """Saves active document to psd (copy) or png or jpg + Args: image_path(string): full local path ext: as_copy: Returns: None """ - self.websocketserver.call(self.client.call - ('Photoshop.saveAs', - image_path=image_path, - ext=ext, - as_copy=as_copy)) + self.websocketserver.call( + self.client.call( + 'Photoshop.saveAs', + image_path=image_path, + ext=ext, + as_copy=as_copy + ) + ) def set_visible(self, layer_id, visibility): - """ - Set layer with 'layer_id' to 'visibility' + """Set layer with 'layer_id' to 'visibility' + Args: layer_id: visibility: Returns: None """ - self.websocketserver.call(self.client.call - ('Photoshop.set_visible', - layer_id=layer_id, - visibility=visibility)) + self.websocketserver.call( + self.client.call( + 'Photoshop.set_visible', + layer_id=layer_id, + visibility=visibility + ) + ) def get_layers_metadata(self): - """ - Reads layers metadata from Headline from active document in PS. - (Headline accessible by File > File Info) - - Returns: - (string): - json documents - example: - {"8":{"active":true,"subset":"imageBG", - "family":"image","id":"pyblish.avalon.instance", - "asset":"Town"}} - 8 is layer(group) id - used for deletion, update etc. + """Reads layers metadata from Headline from active document in PS. + (Headline accessible by File > File Info) + + Returns: + (string): - json documents + example: + {"8":{"active":true,"subset":"imageBG", + "family":"image","id":"pyblish.avalon.instance", + "asset":"Town"}} + 8 is layer(group) id - used for deletion, update etc. """ layers_data = {} res = self.websocketserver.call(self.client.call('Photoshop.read')) @@ -337,8 +350,10 @@ def get_layers_metadata(self): if not isinstance(layers_data, dict): temp_layers_meta = {} for layer_meta in layers_data: - layer_id = layer_meta.get("uuid") or \ - (layer_meta.get("members")[0]) + layer_id = layer_meta.get("uuid") + if not layer_id: + layer_id = layer_meta.get("members")[0] + temp_layers_meta[layer_id] = layer_meta layers_data = temp_layers_meta else: @@ -352,8 +367,7 @@ def get_layers_metadata(self): return layers_data def import_smart_object(self, path, layer_name, as_reference=False): - """ - Import the file at `path` as a smart object to active document. + """Import the file at `path` as a smart object to active document. Args: path (str): File path to import. @@ -362,53 +376,62 @@ def import_smart_object(self, path, layer_name, as_reference=False): as_reference (bool): pull in content or reference """ enhanced_name = self.LOADED_ICON + layer_name - res = self.websocketserver.call(self.client.call - ('Photoshop.import_smart_object', - path=path, name=enhanced_name, - as_reference=as_reference - )) + res = self.websocketserver.call( + self.client.call( + 'Photoshop.import_smart_object', + path=path, + name=enhanced_name, + as_reference=as_reference + ) + ) rec = self._to_records(res).pop() if rec: rec.name = rec.name.replace(self.LOADED_ICON, '') return rec def replace_smart_object(self, layer, path, layer_name): - """ - Replace the smart object `layer` with file at `path` - layer_name (str): Unique layer name to differentiate how many times - same smart object was loaded + """Replace the smart object `layer` with file at `path` Args: layer (PSItem): path (str): File to import. + layer_name (str): Unique layer name to differentiate how many times + same smart object was loaded """ enhanced_name = self.LOADED_ICON + layer_name - self.websocketserver.call(self.client.call - ('Photoshop.replace_smart_object', - layer_id=layer.id, - path=path, name=enhanced_name)) + self.websocketserver.call( + self.client.call( + 'Photoshop.replace_smart_object', + layer_id=layer.id, + path=path, + name=enhanced_name + ) + ) def delete_layer(self, layer_id): - """ - Deletes specific layer by it's id. + """Deletes specific layer by it's id. + Args: layer_id (int): id of layer to delete """ - self.websocketserver.call(self.client.call - ('Photoshop.delete_layer', - layer_id=layer_id)) + self.websocketserver.call( + self.client.call('Photoshop.delete_layer', layer_id=layer_id) + ) def rename_layer(self, layer_id, name): - """ - Renames specific layer by it's id. + """Renames specific layer by it's id. + Args: layer_id (int): id of layer to delete name (str): new name """ - self.websocketserver.call(self.client.call - ('Photoshop.rename_layer', - layer_id=layer_id, - name=name)) + self.websocketserver.call( + self.client.call( + 'Photoshop.rename_layer', + layer_id=layer_id, + name=name + ) + ) def remove_instance(self, instance_id): cleaned_data = {} @@ -419,14 +442,15 @@ def remove_instance(self, instance_id): payload = json.dumps(cleaned_data, indent=4) - self.websocketserver.call(self.client.call - ('Photoshop.imprint', payload=payload) - ) + self.websocketserver.call( + self.client.call('Photoshop.imprint', payload=payload) + ) def get_extension_version(self): """Returns version number of installed extension.""" - return self.websocketserver.call(self.client.call - ('Photoshop.get_extension_version')) + return self.websocketserver.call( + self.client.call('Photoshop.get_extension_version') + ) def close(self): """Shutting down PS and process too. @@ -437,11 +461,12 @@ def close(self): self.websocketserver.call(self.client.call('Photoshop.close')) def _to_records(self, res): - """ - Converts string json representation into list of PSItem for - dot notation access to work. + """Converts string json representation into list of PSItem for + dot notation access to work. + Args: res (string): valid json + Returns: """ @@ -456,15 +481,15 @@ def _to_records(self, res): layers_data = [layers_data] for d in layers_data: # currently implemented and expected fields - item = PSItem(d.get('id'), - d.get('name'), - d.get('group'), - d.get('parents'), - d.get('visible'), - d.get('type'), - d.get('members'), - d.get('long_name'), - d.get("color_code")) - - ret.append(item) + ret.append(PSItem( + d.get('id'), + d.get('name'), + d.get('group'), + d.get('parents'), + d.get('visible'), + d.get('type'), + d.get('members'), + d.get('long_name'), + d.get("color_code") + )) return ret From faf7e7bfebb0412ca360ab22373e75b53db612f9 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 10 Jan 2022 23:06:41 +0100 Subject: [PATCH 445/492] extended main thread exection --- openpype/hosts/photoshop/api/launch_logic.py | 71 +++++++++++++++++--- openpype/hosts/photoshop/api/lib.py | 8 ++- 2 files changed, 67 insertions(+), 12 deletions(-) diff --git a/openpype/hosts/photoshop/api/launch_logic.py b/openpype/hosts/photoshop/api/launch_logic.py index 8e0d00636ab..16a1d232440 100644 --- a/openpype/hosts/photoshop/api/launch_logic.py +++ b/openpype/hosts/photoshop/api/launch_logic.py @@ -2,7 +2,6 @@ import subprocess import collections import asyncio -import functools from wsrpc_aiohttp import ( WebSocketRoute, @@ -26,6 +25,61 @@ class ConnectionNotEstablishedYet(Exception): pass +class MainThreadItem: + """Structure to store information about callback in main thread. + + Item should be used to execute callback in main thread which may be needed + for execution of Qt objects. + + Item store callback (callable variable), arguments and keyword arguments + for the callback. Item hold information about it's process. + """ + not_set = object() + + def __init__(self, callback, *args, **kwargs): + self._done = False + self._exception = self.not_set + self._result = self.not_set + self._callback = callback + self._args = args + self._kwargs = kwargs + + @property + def done(self): + return self._done + + @property + def exception(self): + return self._exception + + @property + def result(self): + return self._result + + def execute(self): + """Execute callback and store it's result. + + Method must be called from main thread. Item is marked as `done` + when callback execution finished. Store output of callback of exception + information when callback raise one. + """ + log.debug("Executing process in main thread") + if self.done: + log.warning("- item is already processed") + return + + log.info("Running callback: {}".format(str(self._callback))) + try: + result = self._callback(*self._args, **self._kwargs) + self._result = result + + except Exception as exc: + self._exception = exc + + finally: + self._done = True + + def stub(): """ Convenience function to get server RPC stub to call methods directed @@ -113,8 +167,10 @@ def is_host_connected(self): return None @classmethod - def execute_in_main_thread(cls, callback): - cls._main_thread_callbacks.append(callback) + def execute_in_main_thread(cls, callback, *args, **kwargs): + item = MainThreadItem(callback, *args, **kwargs) + cls._main_thread_callbacks.append(item) + return item def start(self): if self._started: @@ -145,8 +201,8 @@ def _on_loop_timer(self): cls = self.__class__ for _ in range(len(cls._main_thread_callbacks)): if cls._main_thread_callbacks: - callback = cls._main_thread_callbacks.popleft() - callback() + item = cls._main_thread_callbacks.popleft() + item.execute() if not self.is_process_running: self.log.info("Host process is not running. Closing") @@ -303,10 +359,7 @@ async def experimental_tools_route(self): def _tool_route(self, _tool_name): """The address accessed when clicking on the buttons.""" - partial_method = functools.partial(show_tool_by_name, - _tool_name) - - ProcessLauncher.execute_in_main_thread(partial_method) + ProcessLauncher.execute_in_main_thread(show_tool_by_name, _tool_name) # Required return statement. return "nothing" diff --git a/openpype/hosts/photoshop/api/lib.py b/openpype/hosts/photoshop/api/lib.py index 0938febf435..4c80c04caea 100644 --- a/openpype/hosts/photoshop/api/lib.py +++ b/openpype/hosts/photoshop/api/lib.py @@ -34,17 +34,19 @@ def main(*subprocess_args): launcher.start() if os.environ.get("HEADLESS_PUBLISH"): - launcher.execute_in_main_thread(lambda: headless_publish( + launcher.execute_in_main_thread( + headless_publish, log, "ClosePS", - os.environ.get("IS_TEST"))) + os.environ.get("IS_TEST") + ) elif os.environ.get("AVALON_PHOTOSHOP_WORKFILES_ON_LAUNCH", True): save = False if os.getenv("WORKFILES_SAVE_AS"): save = True launcher.execute_in_main_thread( - lambda: host_tools.show_workfiles(save=save) + host_tools.show_workfiles, save=save ) sys.exit(app.exec_()) From 1b36e7e73039169164b98cf1408134f43b634541 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 10 Jan 2022 23:07:29 +0100 Subject: [PATCH 446/492] added code from openpype __init__.py to pipeline.py --- openpype/hosts/photoshop/api/pipeline.py | 126 +++++++++++++++++------ 1 file changed, 95 insertions(+), 31 deletions(-) diff --git a/openpype/hosts/photoshop/api/pipeline.py b/openpype/hosts/photoshop/api/pipeline.py index ade144e6d43..ed7b94e249a 100644 --- a/openpype/hosts/photoshop/api/pipeline.py +++ b/openpype/hosts/photoshop/api/pipeline.py @@ -1,8 +1,61 @@ -from .. import api, pipeline -from . import lib -from ..vendor import Qt +import os +import sys +from Qt import QtWidgets import pyblish.api +import avalon.api +from avalon import pipeline, io + +from openpype.api import Logger +import openpype.hosts.photoshop + +from . import lib + +log = Logger.get_logger(__name__) + +HOST_DIR = os.path.dirname(os.path.abspath(openpype.hosts.photoshop.__file__)) +PLUGINS_DIR = os.path.join(HOST_DIR, "plugins") +PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish") +LOAD_PATH = os.path.join(PLUGINS_DIR, "load") +CREATE_PATH = os.path.join(PLUGINS_DIR, "create") +INVENTORY_PATH = os.path.join(PLUGINS_DIR, "inventory") + + +def check_inventory(): + if not lib.any_outdated(): + return + + host = avalon.api.registered_host() + outdated_containers = [] + for container in host.ls(): + representation = container['representation'] + representation_doc = io.find_one( + { + "_id": io.ObjectId(representation), + "type": "representation" + }, + projection={"parent": True} + ) + if representation_doc and not lib.is_latest(representation_doc): + outdated_containers.append(container) + + # Warn about outdated containers. + print("Starting new QApplication..") + + message_box = QtWidgets.QMessageBox() + message_box.setIcon(QtWidgets.QMessageBox.Warning) + msg = "There are outdated containers in the scene." + message_box.setText(msg) + message_box.exec_() + + +def on_application_launch(): + check_inventory() + + +def on_pyblish_instance_toggled(instance, old_value, new_value): + """Toggle layer visibility on instance toggles.""" + instance[0].Visible = new_value def install(): @@ -10,9 +63,26 @@ def install(): This function is called automatically on calling `api.install(photoshop)`. """ - print("Installing Avalon Photoshop...") + log.info("Installing OpenPype Photoshop...") pyblish.api.register_host("photoshop") + pyblish.api.register_plugin_path(PUBLISH_PATH) + avalon.api.register_plugin_path(avalon.api.Loader, LOAD_PATH) + avalon.api.register_plugin_path(avalon.api.Creator, CREATE_PATH) + log.info(PUBLISH_PATH) + + pyblish.api.register_callback( + "instanceToggled", on_pyblish_instance_toggled + ) + + avalon.api.on("application.launched", on_application_launch) + + +def uninstall(): + pyblish.api.deregister_plugin_path(PUBLISH_PATH) + avalon.api.deregister_plugin_path(avalon.api.Loader, LOAD_PATH) + avalon.api.deregister_plugin_path(avalon.api.Creator, CREATE_PATH) + def ls(): """Yields containers from active Photoshop document @@ -54,16 +124,14 @@ def ls(): def list_instances(): - """ - List all created instances from current workfile which - will be published. + """List all created instances to publish from current workfile. - Pulls from File > File Info + Pulls from File > File Info - For SubsetManager + For SubsetManager - Returns: - (list) of dictionaries matching instances format + Returns: + (list) of dictionaries matching instances format """ stub = _get_stub() @@ -74,8 +142,8 @@ def list_instances(): layers_meta = stub.get_layers_metadata() if layers_meta: for key, instance in layers_meta.items(): - if instance.get("schema") and \ - "container" in instance.get("schema"): + schema = instance.get("schema") + if schema and "container" in schema: continue instance['uuid'] = key @@ -85,16 +153,15 @@ def list_instances(): def remove_instance(instance): - """ - Remove instance from current workfile metadata. + """Remove instance from current workfile metadata. - Updates metadata of current file in File > File Info and removes - icon highlight on group layer. + Updates metadata of current file in File > File Info and removes + icon highlight on group layer. - For SubsetManager + For SubsetManager - Args: - instance (dict): instance representation from subsetmanager model + Args: + instance (dict): instance representation from subsetmanager model """ stub = _get_stub() @@ -109,8 +176,8 @@ def remove_instance(instance): def _get_stub(): - """ - Handle pulling stub from PS to run operations on host + """Handle pulling stub from PS to run operations on host + Returns: (PhotoshopServerStub) or None """ @@ -126,7 +193,7 @@ def _get_stub(): return stub -class Creator(api.Creator): +class Creator(avalon.api.Creator): """Creator plugin to create instances in Photoshop A LayerSet is created to support any number of layers in an instance. If @@ -140,8 +207,8 @@ def process(self): stub = lib.stub() # only after Photoshop is up for layer in stub.get_layers(): if self.name.lower() == layer.Name.lower(): - msg = Qt.QtWidgets.QMessageBox() - msg.setIcon(Qt.QtWidgets.QMessageBox.Warning) + msg = QtWidgets.QMessageBox() + msg.setIcon(QtWidgets.QMessageBox.Warning) msg.setText(msg) msg.exec_() return False @@ -160,12 +227,9 @@ def process(self): return group -def containerise(name, - namespace, - layer, - context, - loader=None, - suffix="_CON"): +def containerise( + name, namespace, layer, context, loader=None, suffix="_CON" +): """Imprint layer with metadata Containerisation enables a tracking of version, author and origin From 56446e0c4c4dc51ebbb600fb4656d7a1a851f0cf Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 10 Jan 2022 23:07:45 +0100 Subject: [PATCH 447/492] changed registered host --- openpype/hosts/photoshop/api/lib.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/photoshop/api/lib.py b/openpype/hosts/photoshop/api/lib.py index 4c80c04caea..509c5d5c482 100644 --- a/openpype/hosts/photoshop/api/lib.py +++ b/openpype/hosts/photoshop/api/lib.py @@ -1,13 +1,15 @@ import os import sys +import re import contextlib import traceback from Qt import QtWidgets -from openpype.tools.utils import host_tools +import avalon.api from openpype.api import Logger +from openpype.tools.utils import host_tools from openpype.lib.remote_publish import headless_publish from .launch_logic import ProcessLauncher, stub @@ -20,9 +22,9 @@ def safe_excepthook(*args): def main(*subprocess_args): - from avalon import api, photoshop + from openpype.hosts.photoshop import api - api.install(photoshop) + avalon.api.install(api) sys.excepthook = safe_excepthook # coloring in ConsoleTrayApp From c1d6eaa5f948f823eea373b723a405176c27278b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 10 Jan 2022 23:08:51 +0100 Subject: [PATCH 448/492] changed import of photoshop in plugins --- openpype/hosts/photoshop/plugins/create/create_image.py | 2 +- openpype/hosts/photoshop/plugins/load/load_image.py | 3 ++- .../hosts/photoshop/plugins/load/load_image_from_sequence.py | 2 +- openpype/hosts/photoshop/plugins/load/load_reference.py | 3 ++- openpype/hosts/photoshop/plugins/publish/closePS.py | 2 +- .../hosts/photoshop/plugins/publish/collect_current_file.py | 2 +- .../photoshop/plugins/publish/collect_extension_version.py | 2 +- .../hosts/photoshop/plugins/publish/collect_instances.py | 2 +- .../photoshop/plugins/publish/collect_remote_instances.py | 5 +++-- openpype/hosts/photoshop/plugins/publish/collect_workfile.py | 2 +- openpype/hosts/photoshop/plugins/publish/extract_image.py | 2 +- openpype/hosts/photoshop/plugins/publish/extract_review.py | 2 +- .../hosts/photoshop/plugins/publish/extract_save_scene.py | 2 +- .../hosts/photoshop/plugins/publish/increment_workfile.py | 2 +- .../photoshop/plugins/publish/validate_instance_asset.py | 2 +- openpype/hosts/photoshop/plugins/publish/validate_naming.py | 2 +- 16 files changed, 20 insertions(+), 17 deletions(-) diff --git a/openpype/hosts/photoshop/plugins/create/create_image.py b/openpype/hosts/photoshop/plugins/create/create_image.py index 657d41aa93f..cf41bb40202 100644 --- a/openpype/hosts/photoshop/plugins/create/create_image.py +++ b/openpype/hosts/photoshop/plugins/create/create_image.py @@ -1,6 +1,6 @@ from Qt import QtWidgets import openpype.api -from avalon import photoshop +from openpype.hosts.photoshop import api as photoshop class CreateImage(openpype.api.Creator): diff --git a/openpype/hosts/photoshop/plugins/load/load_image.py b/openpype/hosts/photoshop/plugins/load/load_image.py index 981a1ed2049..3756eba54e5 100644 --- a/openpype/hosts/photoshop/plugins/load/load_image.py +++ b/openpype/hosts/photoshop/plugins/load/load_image.py @@ -1,6 +1,7 @@ import re -from avalon import api, photoshop +from avalon import api +from openpype.hosts.photoshop import api as photoshop from openpype.hosts.photoshop.plugins.lib import get_unique_layer_name diff --git a/openpype/hosts/photoshop/plugins/load/load_image_from_sequence.py b/openpype/hosts/photoshop/plugins/load/load_image_from_sequence.py index 8704627b123..158bdc29409 100644 --- a/openpype/hosts/photoshop/plugins/load/load_image_from_sequence.py +++ b/openpype/hosts/photoshop/plugins/load/load_image_from_sequence.py @@ -1,12 +1,12 @@ import os from avalon import api -from avalon import photoshop from avalon.pipeline import get_representation_path_from_context from avalon.vendor import qargparse from openpype.lib import Anatomy from openpype.hosts.photoshop.plugins.lib import get_unique_layer_name +from openpype.hosts.photoshop import api as photoshop stub = photoshop.stub() diff --git a/openpype/hosts/photoshop/plugins/load/load_reference.py b/openpype/hosts/photoshop/plugins/load/load_reference.py index 0cb4e4a69f3..844bb2463a4 100644 --- a/openpype/hosts/photoshop/plugins/load/load_reference.py +++ b/openpype/hosts/photoshop/plugins/load/load_reference.py @@ -1,7 +1,8 @@ import re -from avalon import api, photoshop +from avalon import api +from openpype.hosts.photoshop import api as photoshop from openpype.hosts.photoshop.plugins.lib import get_unique_layer_name stub = photoshop.stub() diff --git a/openpype/hosts/photoshop/plugins/publish/closePS.py b/openpype/hosts/photoshop/plugins/publish/closePS.py index 2f0eab0ee5b..b4ded96001c 100644 --- a/openpype/hosts/photoshop/plugins/publish/closePS.py +++ b/openpype/hosts/photoshop/plugins/publish/closePS.py @@ -4,7 +4,7 @@ import pyblish.api -from avalon import photoshop +from openpype.hosts.photoshop import api as photoshop class ClosePS(pyblish.api.ContextPlugin): diff --git a/openpype/hosts/photoshop/plugins/publish/collect_current_file.py b/openpype/hosts/photoshop/plugins/publish/collect_current_file.py index 4d4829555ec..5daf47c6ac3 100644 --- a/openpype/hosts/photoshop/plugins/publish/collect_current_file.py +++ b/openpype/hosts/photoshop/plugins/publish/collect_current_file.py @@ -2,7 +2,7 @@ import pyblish.api -from avalon import photoshop +from openpype.hosts.photoshop import api as photoshop class CollectCurrentFile(pyblish.api.ContextPlugin): diff --git a/openpype/hosts/photoshop/plugins/publish/collect_extension_version.py b/openpype/hosts/photoshop/plugins/publish/collect_extension_version.py index f07ff0b0ff7..64c99b4fc1e 100644 --- a/openpype/hosts/photoshop/plugins/publish/collect_extension_version.py +++ b/openpype/hosts/photoshop/plugins/publish/collect_extension_version.py @@ -2,7 +2,7 @@ import re import pyblish.api -from avalon import photoshop +from openpype.hosts.photoshop import api as photoshop class CollectExtensionVersion(pyblish.api.ContextPlugin): diff --git a/openpype/hosts/photoshop/plugins/publish/collect_instances.py b/openpype/hosts/photoshop/plugins/publish/collect_instances.py index 5390df768b3..f67cc0cbac8 100644 --- a/openpype/hosts/photoshop/plugins/publish/collect_instances.py +++ b/openpype/hosts/photoshop/plugins/publish/collect_instances.py @@ -1,6 +1,6 @@ import pyblish.api -from avalon import photoshop +from openpype.hosts.photoshop import api as photoshop class CollectInstances(pyblish.api.ContextPlugin): diff --git a/openpype/hosts/photoshop/plugins/publish/collect_remote_instances.py b/openpype/hosts/photoshop/plugins/publish/collect_remote_instances.py index c76e15484ea..e264d04d9fa 100644 --- a/openpype/hosts/photoshop/plugins/publish/collect_remote_instances.py +++ b/openpype/hosts/photoshop/plugins/publish/collect_remote_instances.py @@ -1,10 +1,11 @@ -import pyblish.api import os import re -from avalon import photoshop +import pyblish.api + from openpype.lib import prepare_template_data from openpype.lib.plugin_tools import parse_json +from openpype.hosts.photoshop import api as photoshop class CollectRemoteInstances(pyblish.api.ContextPlugin): diff --git a/openpype/hosts/photoshop/plugins/publish/collect_workfile.py b/openpype/hosts/photoshop/plugins/publish/collect_workfile.py index 88817c39690..db1ede14d5c 100644 --- a/openpype/hosts/photoshop/plugins/publish/collect_workfile.py +++ b/openpype/hosts/photoshop/plugins/publish/collect_workfile.py @@ -1,5 +1,5 @@ -import pyblish.api import os +import pyblish.api class CollectWorkfile(pyblish.api.ContextPlugin): diff --git a/openpype/hosts/photoshop/plugins/publish/extract_image.py b/openpype/hosts/photoshop/plugins/publish/extract_image.py index ae9892e2903..2ba81e0baca 100644 --- a/openpype/hosts/photoshop/plugins/publish/extract_image.py +++ b/openpype/hosts/photoshop/plugins/publish/extract_image.py @@ -1,7 +1,7 @@ import os import openpype.api -from avalon import photoshop +from openpype.hosts.photoshop import api as photoshop class ExtractImage(openpype.api.Extractor): diff --git a/openpype/hosts/photoshop/plugins/publish/extract_review.py b/openpype/hosts/photoshop/plugins/publish/extract_review.py index 8c4d05b282a..1ad442279ab 100644 --- a/openpype/hosts/photoshop/plugins/publish/extract_review.py +++ b/openpype/hosts/photoshop/plugins/publish/extract_review.py @@ -2,7 +2,7 @@ import openpype.api import openpype.lib -from avalon import photoshop +from openpype.hosts.photoshop import api as photoshop class ExtractReview(openpype.api.Extractor): diff --git a/openpype/hosts/photoshop/plugins/publish/extract_save_scene.py b/openpype/hosts/photoshop/plugins/publish/extract_save_scene.py index 0180640c908..03086f389f8 100644 --- a/openpype/hosts/photoshop/plugins/publish/extract_save_scene.py +++ b/openpype/hosts/photoshop/plugins/publish/extract_save_scene.py @@ -1,5 +1,5 @@ import openpype.api -from avalon import photoshop +from openpype.hosts.photoshop import api as photoshop class ExtractSaveScene(openpype.api.Extractor): diff --git a/openpype/hosts/photoshop/plugins/publish/increment_workfile.py b/openpype/hosts/photoshop/plugins/publish/increment_workfile.py index 709fb988fca..92132c393bb 100644 --- a/openpype/hosts/photoshop/plugins/publish/increment_workfile.py +++ b/openpype/hosts/photoshop/plugins/publish/increment_workfile.py @@ -3,7 +3,7 @@ from openpype.action import get_errored_plugins_from_data from openpype.lib import version_up -from avalon import photoshop +from openpype.hosts.photoshop import api as photoshop class IncrementWorkfile(pyblish.api.InstancePlugin): diff --git a/openpype/hosts/photoshop/plugins/publish/validate_instance_asset.py b/openpype/hosts/photoshop/plugins/publish/validate_instance_asset.py index 4dc1972074c..ebe9cc21eae 100644 --- a/openpype/hosts/photoshop/plugins/publish/validate_instance_asset.py +++ b/openpype/hosts/photoshop/plugins/publish/validate_instance_asset.py @@ -1,7 +1,7 @@ from avalon import api import pyblish.api import openpype.api -from avalon import photoshop +from openpype.hosts.photoshop import api as photoshop class ValidateInstanceAssetRepair(pyblish.api.Action): diff --git a/openpype/hosts/photoshop/plugins/publish/validate_naming.py b/openpype/hosts/photoshop/plugins/publish/validate_naming.py index 1635096f4b2..b40e44d016a 100644 --- a/openpype/hosts/photoshop/plugins/publish/validate_naming.py +++ b/openpype/hosts/photoshop/plugins/publish/validate_naming.py @@ -2,7 +2,7 @@ import pyblish.api import openpype.api -from avalon import photoshop +from openpype.hosts.photoshop import api as photoshop class ValidateNamingRepair(pyblish.api.Action): From 551d40b62487618f2c821c40b034c93c92ae2bce Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 10 Jan 2022 23:09:08 +0100 Subject: [PATCH 449/492] changed from where is 'main' imported --- openpype/scripts/non_python_host_launch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/scripts/non_python_host_launch.py b/openpype/scripts/non_python_host_launch.py index 32c4b23f4f3..6b17e6a037d 100644 --- a/openpype/scripts/non_python_host_launch.py +++ b/openpype/scripts/non_python_host_launch.py @@ -81,7 +81,7 @@ def main(argv): host_name = os.environ["AVALON_APP"].lower() if host_name == "photoshop": - from avalon.photoshop.lib import main + from openpype.hosts.photoshop.api.lib import main elif host_name == "aftereffects": from avalon.aftereffects.lib import main elif host_name == "harmony": From d7bc9c4124c38b4f5ae804508939afaf48f572d1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 10 Jan 2022 23:17:50 +0100 Subject: [PATCH 450/492] moved lib functions outside of plugins dir --- openpype/hosts/photoshop/api/__init__.py | 7 ++++++- openpype/hosts/photoshop/{plugins/lib.py => api/plugin.py} | 0 openpype/hosts/photoshop/plugins/__init__.py | 0 openpype/hosts/photoshop/plugins/load/load_image.py | 2 +- .../photoshop/plugins/load/load_image_from_sequence.py | 2 +- openpype/hosts/photoshop/plugins/load/load_reference.py | 2 +- 6 files changed, 9 insertions(+), 4 deletions(-) rename openpype/hosts/photoshop/{plugins/lib.py => api/plugin.py} (100%) delete mode 100644 openpype/hosts/photoshop/plugins/__init__.py diff --git a/openpype/hosts/photoshop/api/__init__.py b/openpype/hosts/photoshop/api/__init__.py index 43756b9ee4f..a25dfe70443 100644 --- a/openpype/hosts/photoshop/api/__init__.py +++ b/openpype/hosts/photoshop/api/__init__.py @@ -12,7 +12,9 @@ install, containerise ) - +from .plugin import ( + get_unique_layer_name +) from .workio import ( file_extensions, has_unsaved_changes, @@ -38,6 +40,9 @@ "install", "containerise", + # Plugin + "get_unique_layer_name", + # workfiles "file_extensions", "has_unsaved_changes", diff --git a/openpype/hosts/photoshop/plugins/lib.py b/openpype/hosts/photoshop/api/plugin.py similarity index 100% rename from openpype/hosts/photoshop/plugins/lib.py rename to openpype/hosts/photoshop/api/plugin.py diff --git a/openpype/hosts/photoshop/plugins/__init__.py b/openpype/hosts/photoshop/plugins/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openpype/hosts/photoshop/plugins/load/load_image.py b/openpype/hosts/photoshop/plugins/load/load_image.py index 3756eba54e5..25f47b02570 100644 --- a/openpype/hosts/photoshop/plugins/load/load_image.py +++ b/openpype/hosts/photoshop/plugins/load/load_image.py @@ -2,8 +2,8 @@ from avalon import api from openpype.hosts.photoshop import api as photoshop +from openpype.hosts.photoshop.api import get_unique_layer_name -from openpype.hosts.photoshop.plugins.lib import get_unique_layer_name stub = photoshop.stub() diff --git a/openpype/hosts/photoshop/plugins/load/load_image_from_sequence.py b/openpype/hosts/photoshop/plugins/load/load_image_from_sequence.py index 158bdc29409..bbf4c602425 100644 --- a/openpype/hosts/photoshop/plugins/load/load_image_from_sequence.py +++ b/openpype/hosts/photoshop/plugins/load/load_image_from_sequence.py @@ -5,8 +5,8 @@ from avalon.vendor import qargparse from openpype.lib import Anatomy -from openpype.hosts.photoshop.plugins.lib import get_unique_layer_name from openpype.hosts.photoshop import api as photoshop +from openpype.hosts.photoshop.api import get_unique_layer_name stub = photoshop.stub() diff --git a/openpype/hosts/photoshop/plugins/load/load_reference.py b/openpype/hosts/photoshop/plugins/load/load_reference.py index 844bb2463a4..0f3c1481551 100644 --- a/openpype/hosts/photoshop/plugins/load/load_reference.py +++ b/openpype/hosts/photoshop/plugins/load/load_reference.py @@ -3,7 +3,7 @@ from avalon import api from openpype.hosts.photoshop import api as photoshop -from openpype.hosts.photoshop.plugins.lib import get_unique_layer_name +from openpype.hosts.photoshop.api import get_unique_layer_name stub = photoshop.stub() From ab1b2bdd7d1f82360a2e066fc704581af6b3fd66 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 10 Jan 2022 23:19:53 +0100 Subject: [PATCH 451/492] moved getting of stub to default photoshop loader class instead of loading it in global scope --- openpype/hosts/photoshop/api/__init__.py | 12 +++--- openpype/hosts/photoshop/api/lib.py | 1 - openpype/hosts/photoshop/api/plugin.py | 9 +++++ .../photoshop/plugins/load/load_image.py | 27 +++++++------ .../plugins/load/load_image_from_sequence.py | 20 ++++------ .../photoshop/plugins/load/load_reference.py | 38 ++++++++++--------- 6 files changed, 60 insertions(+), 47 deletions(-) diff --git a/openpype/hosts/photoshop/api/__init__.py b/openpype/hosts/photoshop/api/__init__.py index a25dfe70443..17a371f002e 100644 --- a/openpype/hosts/photoshop/api/__init__.py +++ b/openpype/hosts/photoshop/api/__init__.py @@ -4,6 +4,8 @@ """ +from .launch_logic import stub + from .pipeline import ( ls, list_instances, @@ -13,6 +15,7 @@ containerise ) from .plugin import ( + PhotoshopLoader, get_unique_layer_name ) from .workio import ( @@ -29,9 +32,10 @@ maintained_visibility ) -from .launch_logic import stub - __all__ = [ + # launch_logic + "stub" + # pipeline "ls", "list_instances", @@ -41,6 +45,7 @@ "containerise", # Plugin + "PhotoshopLoader", "get_unique_layer_name", # workfiles @@ -54,7 +59,4 @@ # lib "maintained_selection", "maintained_visibility", - - # launch_logic - "stub" ] diff --git a/openpype/hosts/photoshop/api/lib.py b/openpype/hosts/photoshop/api/lib.py index 509c5d5c482..707cd476c51 100644 --- a/openpype/hosts/photoshop/api/lib.py +++ b/openpype/hosts/photoshop/api/lib.py @@ -1,6 +1,5 @@ import os import sys -import re import contextlib import traceback diff --git a/openpype/hosts/photoshop/api/plugin.py b/openpype/hosts/photoshop/api/plugin.py index 74aff061147..c577c67d82a 100644 --- a/openpype/hosts/photoshop/api/plugin.py +++ b/openpype/hosts/photoshop/api/plugin.py @@ -1,5 +1,8 @@ import re +import avalon.api +from .launch_logic import stub + def get_unique_layer_name(layers, asset_name, subset_name): """ @@ -24,3 +27,9 @@ def get_unique_layer_name(layers, asset_name, subset_name): occurrences = names.get(name, 0) return "{}_{:0>3d}".format(name, occurrences + 1) + + +class PhotoshopLoader(avalon.api.Loader): + @staticmethod + def get_stub(): + return stub() diff --git a/openpype/hosts/photoshop/plugins/load/load_image.py b/openpype/hosts/photoshop/plugins/load/load_image.py index 25f47b02570..3b1cfe96360 100644 --- a/openpype/hosts/photoshop/plugins/load/load_image.py +++ b/openpype/hosts/photoshop/plugins/load/load_image.py @@ -5,9 +5,7 @@ from openpype.hosts.photoshop.api import get_unique_layer_name -stub = photoshop.stub() - -class ImageLoader(api.Loader): +class ImageLoader(photoshop.PhotoshopLoader): """Load images Stores the imported asset in a container named after the asset. @@ -17,11 +15,14 @@ class ImageLoader(api.Loader): representations = ["*"] def load(self, context, name=None, namespace=None, data=None): - layer_name = get_unique_layer_name(stub.get_layers(), - context["asset"]["name"], - name) + stub = self.get_stub() + layer_name = get_unique_layer_name( + stub.get_layers(), + context["asset"]["name"], + name + ) with photoshop.maintained_selection(): - layer = self.import_layer(self.fname, layer_name) + layer = self.import_layer(self.fname, layer_name, stub) self[:] = [layer] namespace = namespace or layer_name @@ -36,6 +37,8 @@ def load(self, context, name=None, namespace=None, data=None): def update(self, container, representation): """ Switch asset or change version """ + stub = self.get_stub() + layer = container.pop("layer") context = representation.get("context", {}) @@ -45,9 +48,9 @@ def update(self, container, representation): layer_name = "{}_{}".format(context["asset"], context["subset"]) # switching assets if namespace_from_container != layer_name: - layer_name = get_unique_layer_name(stub.get_layers(), - context["asset"], - context["subset"]) + layer_name = get_unique_layer_name( + stub.get_layers(), context["asset"], context["subset"] + ) else: # switching version - keep same name layer_name = container["namespace"] @@ -67,6 +70,8 @@ def remove(self, container): Args: container (dict): container to be removed - used to get layer_id """ + stub = self.get_stub() + layer = container.pop("layer") stub.imprint(layer, {}) stub.delete_layer(layer.id) @@ -74,5 +79,5 @@ def remove(self, container): def switch(self, container, representation): self.update(container, representation) - def import_layer(self, file_name, layer_name): + def import_layer(self, file_name, layer_name, stub): return stub.import_smart_object(file_name, layer_name) diff --git a/openpype/hosts/photoshop/plugins/load/load_image_from_sequence.py b/openpype/hosts/photoshop/plugins/load/load_image_from_sequence.py index bbf4c602425..ab4682e63e4 100644 --- a/openpype/hosts/photoshop/plugins/load/load_image_from_sequence.py +++ b/openpype/hosts/photoshop/plugins/load/load_image_from_sequence.py @@ -1,17 +1,13 @@ import os -from avalon import api from avalon.pipeline import get_representation_path_from_context from avalon.vendor import qargparse -from openpype.lib import Anatomy from openpype.hosts.photoshop import api as photoshop from openpype.hosts.photoshop.api import get_unique_layer_name -stub = photoshop.stub() - -class ImageFromSequenceLoader(api.Loader): +class ImageFromSequenceLoader(photoshop.PhotoshopLoader): """ Load specifing image from sequence Used only as quick load of reference file from a sequence. @@ -35,15 +31,16 @@ class ImageFromSequenceLoader(api.Loader): def load(self, context, name=None, namespace=None, data=None): if data.get("frame"): - self.fname = os.path.join(os.path.dirname(self.fname), - data["frame"]) + self.fname = os.path.join( + os.path.dirname(self.fname), data["frame"] + ) if not os.path.exists(self.fname): return - stub = photoshop.stub() - layer_name = get_unique_layer_name(stub.get_layers(), - context["asset"]["name"], - name) + stub = self.get_stub() + layer_name = get_unique_layer_name( + stub.get_layers(), context["asset"]["name"], name + ) with photoshop.maintained_selection(): layer = stub.import_smart_object(self.fname, layer_name) @@ -95,4 +92,3 @@ def update(self, container, representation): def remove(self, container): """No update possible, not containerized.""" pass - diff --git a/openpype/hosts/photoshop/plugins/load/load_reference.py b/openpype/hosts/photoshop/plugins/load/load_reference.py index 0f3c1481551..60142d4a1ff 100644 --- a/openpype/hosts/photoshop/plugins/load/load_reference.py +++ b/openpype/hosts/photoshop/plugins/load/load_reference.py @@ -5,27 +5,26 @@ from openpype.hosts.photoshop import api as photoshop from openpype.hosts.photoshop.api import get_unique_layer_name -stub = photoshop.stub() - -class ReferenceLoader(api.Loader): +class ReferenceLoader(photoshop.PhotoshopLoader): """Load reference images - Stores the imported asset in a container named after the asset. + Stores the imported asset in a container named after the asset. - Inheriting from 'load_image' didn't work because of - "Cannot write to closing transport", possible refactor. + Inheriting from 'load_image' didn't work because of + "Cannot write to closing transport", possible refactor. """ families = ["image", "render"] representations = ["*"] def load(self, context, name=None, namespace=None, data=None): - layer_name = get_unique_layer_name(stub.get_layers(), - context["asset"]["name"], - name) + stub = self.get_stub() + layer_name = get_unique_layer_name( + stub.get_layers(), context["asset"]["name"], name + ) with photoshop.maintained_selection(): - layer = self.import_layer(self.fname, layer_name) + layer = self.import_layer(self.fname, layer_name, stub) self[:] = [layer] namespace = namespace or layer_name @@ -40,6 +39,7 @@ def load(self, context, name=None, namespace=None, data=None): def update(self, container, representation): """ Switch asset or change version """ + stub = self.get_stub() layer = container.pop("layer") context = representation.get("context", {}) @@ -49,9 +49,9 @@ def update(self, container, representation): layer_name = "{}_{}".format(context["asset"], context["subset"]) # switching assets if namespace_from_container != layer_name: - layer_name = get_unique_layer_name(stub.get_layers(), - context["asset"], - context["subset"]) + layer_name = get_unique_layer_name( + stub.get_layers(), context["asset"], context["subset"] + ) else: # switching version - keep same name layer_name = container["namespace"] @@ -66,11 +66,12 @@ def update(self, container, representation): ) def remove(self, container): - """ - Removes element from scene: deletes layer + removes from Headline + """Removes element from scene: deletes layer + removes from Headline + Args: container (dict): container to be removed - used to get layer_id """ + stub = self.get_stub() layer = container.pop("layer") stub.imprint(layer, {}) stub.delete_layer(layer.id) @@ -78,6 +79,7 @@ def remove(self, container): def switch(self, container, representation): self.update(container, representation) - def import_layer(self, file_name, layer_name): - return stub.import_smart_object(file_name, layer_name, - as_reference=True) + def import_layer(self, file_name, layer_name, stub): + return stub.import_smart_object( + file_name, layer_name, as_reference=True + ) From c2b6cf8714a8caf5d6e49efbb44b386fdcdf713a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 10 Jan 2022 23:38:32 +0100 Subject: [PATCH 452/492] fixed init file --- openpype/hosts/photoshop/api/__init__.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/photoshop/api/__init__.py b/openpype/hosts/photoshop/api/__init__.py index 17a371f002e..4cc2aa2c781 100644 --- a/openpype/hosts/photoshop/api/__init__.py +++ b/openpype/hosts/photoshop/api/__init__.py @@ -10,12 +10,13 @@ ls, list_instances, remove_instance, - Creator, install, + uninstall, containerise ) from .plugin import ( PhotoshopLoader, + Creator, get_unique_layer_name ) from .workio import ( @@ -34,18 +35,18 @@ __all__ = [ # launch_logic - "stub" + "stub", # pipeline "ls", "list_instances", "remove_instance", - "Creator", "install", "containerise", # Plugin "PhotoshopLoader", + "Creator", "get_unique_layer_name", # workfiles From b6a5123210d8f278cbb320f7378bce877c798949 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 10 Jan 2022 23:38:43 +0100 Subject: [PATCH 453/492] moved Creator to plugin.py --- openpype/hosts/photoshop/api/pipeline.py | 34 ------------------------ openpype/hosts/photoshop/api/plugin.py | 34 ++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/openpype/hosts/photoshop/api/pipeline.py b/openpype/hosts/photoshop/api/pipeline.py index ed7b94e249a..25983f24713 100644 --- a/openpype/hosts/photoshop/api/pipeline.py +++ b/openpype/hosts/photoshop/api/pipeline.py @@ -193,40 +193,6 @@ def _get_stub(): return stub -class Creator(avalon.api.Creator): - """Creator plugin to create instances in Photoshop - - A LayerSet is created to support any number of layers in an instance. If - the selection is used, these layers will be added to the LayerSet. - """ - - def process(self): - # Photoshop can have multiple LayerSets with the same name, which does - # not work with Avalon. - msg = "Instance with name \"{}\" already exists.".format(self.name) - stub = lib.stub() # only after Photoshop is up - for layer in stub.get_layers(): - if self.name.lower() == layer.Name.lower(): - msg = QtWidgets.QMessageBox() - msg.setIcon(QtWidgets.QMessageBox.Warning) - msg.setText(msg) - msg.exec_() - return False - - # Store selection because adding a group will change selection. - with lib.maintained_selection(): - - # Add selection to group. - if (self.options or {}).get("useSelection"): - group = stub.group_selected_layers(self.name) - else: - group = stub.create_group(self.name) - - stub.imprint(group, self.data) - - return group - - def containerise( name, namespace, layer, context, loader=None, suffix="_CON" ): diff --git a/openpype/hosts/photoshop/api/plugin.py b/openpype/hosts/photoshop/api/plugin.py index c577c67d82a..e0db67de2c3 100644 --- a/openpype/hosts/photoshop/api/plugin.py +++ b/openpype/hosts/photoshop/api/plugin.py @@ -33,3 +33,37 @@ class PhotoshopLoader(avalon.api.Loader): @staticmethod def get_stub(): return stub() + + +class Creator(avalon.api.Creator): + """Creator plugin to create instances in Photoshop + + A LayerSet is created to support any number of layers in an instance. If + the selection is used, these layers will be added to the LayerSet. + """ + + def process(self): + # Photoshop can have multiple LayerSets with the same name, which does + # not work with Avalon. + msg = "Instance with name \"{}\" already exists.".format(self.name) + stub = lib.stub() # only after Photoshop is up + for layer in stub.get_layers(): + if self.name.lower() == layer.Name.lower(): + msg = QtWidgets.QMessageBox() + msg.setIcon(QtWidgets.QMessageBox.Warning) + msg.setText(msg) + msg.exec_() + return False + + # Store selection because adding a group will change selection. + with lib.maintained_selection(): + + # Add selection to group. + if (self.options or {}).get("useSelection"): + group = stub.group_selected_layers(self.name) + else: + group = stub.create_group(self.name) + + stub.imprint(group, self.data) + + return group From 100ff46421ceb688e7d2e20dec58f43d20f5902b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 11 Jan 2022 13:27:16 +0100 Subject: [PATCH 454/492] OP-2049 - fix frame content for sequence starting with 0 Previously expression didn't trigger as repre.get("frameStart") returned 0 which translated into False --- openpype/plugins/publish/integrate_new.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/integrate_new.py b/openpype/plugins/publish/integrate_new.py index 1b0b8da2ff1..cec2e470b34 100644 --- a/openpype/plugins/publish/integrate_new.py +++ b/openpype/plugins/publish/integrate_new.py @@ -580,7 +580,7 @@ def register(self, instance): if repre.get("outputName"): representation["context"]["output"] = repre['outputName'] - if sequence_repre and repre.get("frameStart"): + if sequence_repre and repre.get("frameStart") is not None: representation['context']['frame'] = ( dst_padding_exp % int(repre.get("frameStart")) ) From 74a4fbfea60ff34866000a49962a26d40e6b9fd8 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 11 Jan 2022 17:22:24 +0100 Subject: [PATCH 455/492] flame: testing export of otio timeline --- .../flame/plugins/publish/collect_test_selection.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/flame/plugins/publish/collect_test_selection.py b/openpype/hosts/flame/plugins/publish/collect_test_selection.py index 0c75b3204fe..3e3ff27035e 100644 --- a/openpype/hosts/flame/plugins/publish/collect_test_selection.py +++ b/openpype/hosts/flame/plugins/publish/collect_test_selection.py @@ -1,7 +1,9 @@ import os import pyblish.api +import tempfile import openpype.hosts.flame.api as opfapi from openpype.hosts.flame.otio import flame_export as otio_export +import opentimelineio as otio from pprint import pformat reload(otio_export) # noqa @@ -25,16 +27,22 @@ def process(self, context): self.test_otio_export(sequence) def test_otio_export(self, sequence): - home_dir = os.path.expanduser("~") + test_dir = os.path.normpath( + tempfile.mkdtemp(prefix="test_pyblish_tmp_") + ) export_path = os.path.normpath( os.path.join( - home_dir, "otio_timeline_export.otio" + test_dir, "otio_timeline_export.otio" ) ) otio_timeline = otio_export.create_otio_timeline(sequence) otio_export.write_to_file( otio_timeline, export_path ) + read_timeline_otio = otio.adapters.read_from_file(export_path) + + if otio_timeline != read_timeline_otio: + raise Exception("Exported otio timeline is different from original") self.log.info(pformat(otio_timeline)) self.log.info("Otio exported to: {}".format(export_path)) From 896ba23730a0fdedbffe5820e036d058a7e36d39 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 11 Jan 2022 17:23:56 +0100 Subject: [PATCH 456/492] flame: hound fixes --- .../hosts/flame/plugins/publish/collect_test_selection.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/flame/plugins/publish/collect_test_selection.py b/openpype/hosts/flame/plugins/publish/collect_test_selection.py index 3e3ff27035e..73401368b1d 100644 --- a/openpype/hosts/flame/plugins/publish/collect_test_selection.py +++ b/openpype/hosts/flame/plugins/publish/collect_test_selection.py @@ -28,7 +28,7 @@ def process(self, context): def test_otio_export(self, sequence): test_dir = os.path.normpath( - tempfile.mkdtemp(prefix="test_pyblish_tmp_") + tempfile.mkdtemp(prefix="test_pyblish_tmp_") ) export_path = os.path.normpath( os.path.join( @@ -42,7 +42,7 @@ def test_otio_export(self, sequence): read_timeline_otio = otio.adapters.read_from_file(export_path) if otio_timeline != read_timeline_otio: - raise Exception("Exported otio timeline is different from original") + raise Exception("Exported timeline is different from original") self.log.info(pformat(otio_timeline)) self.log.info("Otio exported to: {}".format(export_path)) From 66cedb61e929ae475ce525bb538a8d3166471a52 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 11 Jan 2022 17:29:11 +0100 Subject: [PATCH 457/492] flame: addressing CTX.apps renamed to CTX.flame_apps --- openpype/hosts/flame/api/lib.py | 2 +- .../api/utility_scripts/openpype_in_flame.py | 23 ++++++++++--------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index 2cc9fee1730..3ec57c64341 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -20,7 +20,7 @@ class CTX: # singleton used for passing data between api modules app_framework = None - apps = [] + flame_apps = [] selection = None diff --git a/openpype/hosts/flame/api/utility_scripts/openpype_in_flame.py b/openpype/hosts/flame/api/utility_scripts/openpype_in_flame.py index c385fbb8cbf..72614f2b5d6 100644 --- a/openpype/hosts/flame/api/utility_scripts/openpype_in_flame.py +++ b/openpype/hosts/flame/api/utility_scripts/openpype_in_flame.py @@ -45,14 +45,14 @@ def exeption_handler(exctype, value, _traceback): def cleanup(): """Cleaning up Flame framework context """ - if opfapi.CTX.apps: - print('`{}` cleaning up apps:\n {}\n'.format( - __file__, pformat(opfapi.CTX.apps))) - while len(opfapi.CTX.apps): - app = opfapi.CTX.apps.pop() + if opfapi.CTX.flame_apps: + print('`{}` cleaning up flame_apps:\n {}\n'.format( + __file__, pformat(opfapi.CTX.flame_apps))) + while len(opfapi.CTX.flame_apps): + app = opfapi.CTX.flame_apps.pop() print('`{}` removing : {}'.format(__file__, app.name)) del app - opfapi.CTX.apps = [] + opfapi.CTX.flame_apps = [] if opfapi.CTX.app_framework: print('openpype\t: {} cleaning up'.format( @@ -66,11 +66,12 @@ def cleanup(): def load_apps(): - """Load available apps into Flame framework + """Load available flame_apps into Flame framework """ - opfapi.CTX.apps.append( + opfapi.CTX.flame_apps.append( opfapi.FlameMenuProjectConnect(opfapi.CTX.app_framework)) - opfapi.CTX.apps.append(opfapi.FlameMenuTimeline(opfapi.CTX.app_framework)) + opfapi.CTX.flame_apps.append( + opfapi.FlameMenuTimeline(opfapi.CTX.app_framework)) opfapi.CTX.app_framework.log.info("Apps are loaded") @@ -103,7 +104,7 @@ def app_initialized(parent=None): First it needs to test if it can import the flame modul. This will happen only in case a project has been loaded. Then `app_initialized` will load main Framework which will load -all menu objects as apps. +all menu objects as flame_apps. """ try: @@ -131,7 +132,7 @@ def _build_app_menu(app_name): # first find the relative appname app = None - for _app in opfapi.CTX.apps: + for _app in opfapi.CTX.flame_apps: if _app.__class__.__name__ == app_name: app = _app From c025d4c8e4dc5f4c9c4f68e209e990db05f35a89 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 11 Jan 2022 17:34:02 +0100 Subject: [PATCH 458/492] flame: removing constant True return --- openpype/hosts/flame/api/lib.py | 5 +---- openpype/hosts/flame/api/pipeline.py | 3 +-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index 3ec57c64341..dd212297e22 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -374,8 +374,6 @@ def set_segment_data_marker(segment, data=None): # add tag data to marker's comment marker.comment = json.dumps(data) - return True - def set_publish_attribute(segment, value): """ Set Publish attribute in input Tag object @@ -388,8 +386,7 @@ def set_publish_attribute(segment, value): tag_data["publish"] = value # set data to the publish attribute - if not set_segment_data_marker(segment, tag_data): - raise AttributeError("Not imprint data to segment") + set_segment_data_marker(segment, tag_data) def get_publish_attribute(segment): diff --git a/openpype/hosts/flame/api/pipeline.py b/openpype/hosts/flame/api/pipeline.py index b65c85f5df5..30c70b491bd 100644 --- a/openpype/hosts/flame/api/pipeline.py +++ b/openpype/hosts/flame/api/pipeline.py @@ -144,8 +144,7 @@ def imprint(segment, data=None): """ data = data or {} - if not set_segment_data_marker(segment, data): - raise AttributeError("Not imprint data to segment") + set_segment_data_marker(segment, data) # add publish attribute set_publish_attribute(segment, True) From 384edda56fba3ab6ea20b351a81e3fd8e5ea65e8 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 11 Jan 2022 17:39:25 +0100 Subject: [PATCH 459/492] flame: improving code from suggestion --- openpype/hosts/flame/api/lib.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index dd212297e22..7788a6b3f47 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -545,16 +545,16 @@ def get_segment_attributes(segment): "source_duration", "source_in", "source_out" ] segment_attrs_data = {} - for attr in segment_attrs: - if not hasattr(segment, attr): + for attr_name in segment_attrs: + if not hasattr(segment, attr_name): continue - _value = getattr(segment, attr) - segment_attrs_data[attr] = str(_value).replace("+", ":") + attr = getattr(segment, attr_name) + segment_attrs_data[attr] = str(attr).replace("+", ":") if attr in ["record_in", "record_out"]: - clip_data[attr] = _value.relative_frame + clip_data[attr_name] = attr.relative_frame else: - clip_data[attr] = _value.frame + clip_data[attr_name] = attr.frame clip_data["segment_timecodes"] = segment_attrs_data From dbf9c6899632c3ec0ed11da1a1d0e17d35a70dc4 Mon Sep 17 00:00:00 2001 From: OpenPype Date: Wed, 12 Jan 2022 03:43:49 +0000 Subject: [PATCH 460/492] [Automated] Bump version --- CHANGELOG.md | 13 ++++++++++--- openpype/version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20ab0876906..e92c16dc5f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## [3.8.0-nightly.2](https://github.com/pypeclub/OpenPype/tree/HEAD) +## [3.8.0-nightly.3](https://github.com/pypeclub/OpenPype/tree/HEAD) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.7.0...HEAD) @@ -10,23 +10,29 @@ **🚀 Enhancements** +- Photoshop: Move implementation to OpenPype [\#2510](https://github.com/pypeclub/OpenPype/pull/2510) +- TimersManager: Move module one hierarchy higher [\#2501](https://github.com/pypeclub/OpenPype/pull/2501) - Ftrack: Event handlers settings [\#2496](https://github.com/pypeclub/OpenPype/pull/2496) - Tools: Fix style and modality of errors in loader and creator [\#2489](https://github.com/pypeclub/OpenPype/pull/2489) +- Project Manager: Remove project button cleanup [\#2482](https://github.com/pypeclub/OpenPype/pull/2482) - Tools: Be able to change models of tasks and assets widgets [\#2475](https://github.com/pypeclub/OpenPype/pull/2475) - Publish pype: Reduce publish process defering [\#2464](https://github.com/pypeclub/OpenPype/pull/2464) - Maya: Improve speed of Collect History logic [\#2460](https://github.com/pypeclub/OpenPype/pull/2460) - Maya: Validate Rig Controllers - fix Error: in script editor [\#2459](https://github.com/pypeclub/OpenPype/pull/2459) - Maya: Optimize Validate Locked Normals speed for dense polymeshes [\#2457](https://github.com/pypeclub/OpenPype/pull/2457) +- Fix \#2453 Refactor missing \_get\_reference\_node method [\#2455](https://github.com/pypeclub/OpenPype/pull/2455) +- Houdini: Remove broken unique name counter [\#2450](https://github.com/pypeclub/OpenPype/pull/2450) +- Maya: Improve lib.polyConstraint performance when Select tool is not the active tool context [\#2447](https://github.com/pypeclub/OpenPype/pull/2447) - Maya : add option to not group reference in ReferenceLoader [\#2383](https://github.com/pypeclub/OpenPype/pull/2383) **🐛 Bug fixes** - General: Settings work if OpenPypeVersion is available [\#2494](https://github.com/pypeclub/OpenPype/pull/2494) +- General: PYTHONPATH may break OpenPype dependencies [\#2493](https://github.com/pypeclub/OpenPype/pull/2493) - Workfiles tool: Files widget show files on first show [\#2488](https://github.com/pypeclub/OpenPype/pull/2488) - General: Custom template paths filter fix [\#2483](https://github.com/pypeclub/OpenPype/pull/2483) - Loader: Remove always on top flag in tray [\#2480](https://github.com/pypeclub/OpenPype/pull/2480) - General: Anatomy does not return root envs as unicode [\#2465](https://github.com/pypeclub/OpenPype/pull/2465) -- Webpublisher: Fix assignment of families of TVpaint instances [\#2373](https://github.com/pypeclub/OpenPype/pull/2373) **Merged pull requests:** @@ -63,7 +69,6 @@ - Maya add render image path to settings [\#2375](https://github.com/pypeclub/OpenPype/pull/2375) - Hiero: python3 compatibility [\#2365](https://github.com/pypeclub/OpenPype/pull/2365) - Maya: Add is\_static\_image\_plane and is\_in\_all\_views option in imagePlaneLoader [\#2356](https://github.com/pypeclub/OpenPype/pull/2356) -- TVPaint: Move implementation to OpenPype [\#2336](https://github.com/pypeclub/OpenPype/pull/2336) **🐛 Bug fixes** @@ -80,7 +85,9 @@ - hiero: solve custom ocio path [\#2379](https://github.com/pypeclub/OpenPype/pull/2379) - hiero: fix workio and flatten [\#2378](https://github.com/pypeclub/OpenPype/pull/2378) - Nuke: fixing menu re-drawing during context change [\#2374](https://github.com/pypeclub/OpenPype/pull/2374) +- Webpublisher: Fix assignment of families of TVpaint instances [\#2373](https://github.com/pypeclub/OpenPype/pull/2373) - Nuke: fixing node name based on switched asset name [\#2369](https://github.com/pypeclub/OpenPype/pull/2369) +- Tools: Placeholder color [\#2359](https://github.com/pypeclub/OpenPype/pull/2359) - Houdini: Fix HDA creation [\#2350](https://github.com/pypeclub/OpenPype/pull/2350) **Merged pull requests:** diff --git a/openpype/version.py b/openpype/version.py index ed0a96d4de9..1f005d69522 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.8.0-nightly.2" +__version__ = "3.8.0-nightly.3" diff --git a/pyproject.toml b/pyproject.toml index 0ef447e0be8..f9155f05a37 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.8.0-nightly.2" # OpenPype +version = "3.8.0-nightly.3" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From 95be2c3bc8fc5a91ac2f65072fd56a6a30cda872 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 12 Jan 2022 11:16:50 +0100 Subject: [PATCH 461/492] flame: adding pathsep instead of ";" --- openpype/hosts/flame/hooks/pre_flame_setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/flame/hooks/pre_flame_setup.py b/openpype/hosts/flame/hooks/pre_flame_setup.py index d5ddafde0cd..fe8acda257d 100644 --- a/openpype/hosts/flame/hooks/pre_flame_setup.py +++ b/openpype/hosts/flame/hooks/pre_flame_setup.py @@ -85,7 +85,7 @@ def _add_pythonpath(self): pythonpath = self.launch_context.env.get("PYTHONPATH") # separate it explicity by `;` that is what we use in settings - new_pythonpath = self.flame_pythonpath.split(";") + new_pythonpath = self.flame_pythonpath.split(os.pathsep) new_pythonpath += pythonpath.split(os.pathsep) self.launch_context.env["PYTHONPATH"] = os.pathsep.join(new_pythonpath) From c01ed46157fe70346a5a6e3b639624fe6ca551b9 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 12 Jan 2022 14:11:49 +0100 Subject: [PATCH 462/492] added ability to skip 3rd part lib validations --- setup.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 92cc76dc7a4..6891b3c4197 100644 --- a/setup.py +++ b/setup.py @@ -58,7 +58,9 @@ def validate_thirdparty_binaries(): raise RuntimeError(error_msg.format("OpenImageIO")) -validate_thirdparty_binaries() +# Give ability to skip vaidation +if not os.getenv("SKIP_THIRD_PARTY_VALIDATION"): + validate_thirdparty_binaries() version = {} From 95dcc57d0f175490a73e37852238c875cad1816f Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 12 Jan 2022 16:41:51 +0100 Subject: [PATCH 463/492] OP-1730 - add permission to list channels Will be used to delete messages or files --- openpype/modules/slack/manifest.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/modules/slack/manifest.yml b/openpype/modules/slack/manifest.yml index bd920ac2669..7a65cc5915c 100644 --- a/openpype/modules/slack/manifest.yml +++ b/openpype/modules/slack/manifest.yml @@ -18,6 +18,7 @@ oauth_config: - chat:write.customize - chat:write.public - files:write + - channels:read settings: org_deploy_enabled: false socket_mode_enabled: false From 57a51c28a38a6313f3df08e54acefe69f650227a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 12 Jan 2022 16:43:12 +0100 Subject: [PATCH 464/492] OP-1730 - added icon and documentation Slack requires peculiar format of icon, provided one. --- .../modules/slack/resources/openpype_icon.png | Bin 0 -> 105495 bytes website/docs/module_slack.md | 6 ++++++ 2 files changed, 6 insertions(+) create mode 100644 openpype/modules/slack/resources/openpype_icon.png diff --git a/openpype/modules/slack/resources/openpype_icon.png b/openpype/modules/slack/resources/openpype_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..bb38dcf5778d06b75793b70c8c92176062e8c90b GIT binary patch literal 105495 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≫Lb-DaL0}w^*iUAX- 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

-|1{^JYf%l_9# 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 Basic information > Display Information. +You can upload any image you want, or for your convenience locate prepared OpenPype icon in your installed Openpype installation in `openpype\modules\slac\resources`. ## System Settings From e9cbcf917c1cce6984254cbee7e72e4e163568b0 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 12 Jan 2022 16:58:04 +0100 Subject: [PATCH 465/492] OP-1730 - added logging sent messages and uploaded files into db --- .../plugins/publish/integrate_slack_api.py | 65 ++++++++++++------- 1 file changed, 42 insertions(+), 23 deletions(-) diff --git a/openpype/modules/slack/plugins/publish/integrate_slack_api.py b/openpype/modules/slack/plugins/publish/integrate_slack_api.py index 5aba3725492..6afbbf58497 100644 --- a/openpype/modules/slack/plugins/publish/integrate_slack_api.py +++ b/openpype/modules/slack/plugins/publish/integrate_slack_api.py @@ -2,8 +2,10 @@ import six import pyblish.api import copy +from datetime import datetime from openpype.lib.plugin_tools import prepare_template_data +from openpype.lib import OpenPypeMongoConnection class IntegrateSlackAPI(pyblish.api.InstancePlugin): @@ -41,23 +43,38 @@ def process(self, instance): if not message: return - # if message_profile["upload_thumbnail"] and thumbnail_path: - # publish_files.add(thumbnail_path) + if message_profile["upload_thumbnail"] and thumbnail_path: + publish_files.add(thumbnail_path) if message_profile["upload_review"] and review_path: publish_files.add(review_path) + project = instance.context.data["anatomyData"]["project"]["code"] for channel in message_profile["channels"]: if six.PY2: - self._python2_call(instance.data["slack_token"], - channel, - message, - publish_files) + msg_id, file_ids = \ + self._python2_call(instance.data["slack_token"], + channel, + message, + publish_files) else: - self._python3_call(instance.data["slack_token"], - channel, - message, - publish_files) + msg_id, file_ids = \ + self._python3_call(instance.data["slack_token"], + channel, + message, + publish_files) + + msg = { + "type": "slack", + "msg_id": msg_id, + "file_ids": file_ids, + "project": project, + "created_dt": datetime.now() + } + mongo_client = OpenPypeMongoConnection.get_mongo_client() + database_name = os.environ["OPENPYPE_DATABASE_NAME"] + dbcon = mongo_client[database_name]["notification_messages"] + dbcon.insert_one(msg) def _get_filled_message(self, message_templ, instance, review_path=None): """Use message_templ and data from instance to get message content. @@ -85,7 +102,7 @@ def _get_filled_message(self, message_templ, instance, review_path=None): task_data = fill_data.get("task") for key, value in task_data.items(): fill_key = "task[{}]".format(key) - fill_pairs.append((fill_key , value)) + fill_pairs.append((fill_key, value)) fill_pairs.append(("task", task_data["name"])) self.log.debug("fill_pairs ::{}".format(fill_pairs)) @@ -126,23 +143,24 @@ def _get_review_path(self, instance): break return published_path - def _python2_call(self, token, channel, message, - publish_files): + def _python2_call(self, token, channel, message, publish_files): from slackclient import SlackClient try: client = SlackClient(token) - self.log.info("publish {}".format(publish_files)) attachment_str = "\n\n Attachment links: \n" + file_ids = [] for p_file in publish_files: with open(p_file, 'rb') as pf: response = client.api_call( "files.upload", - channels=channel, - file=pf + file=pf, + channel=channel, + title=os.path.basename(p_file) ) attachment_str += "\n<{}|{}>".format( response["file"]["permalink"], os.path.basename(p_file)) + file_ids.append(response["file"]["id"]) if publish_files: message += attachment_str @@ -152,23 +170,24 @@ def _python2_call(self, token, channel, message, channel=channel, text=message ) - self.log.info("repsonse {}".format(response)) if response.get("error"): error_str = self._enrich_error(str(response.get("error")), channel) self.log.warning("Error happened: {}".format(error_str)) + else: + return response["ts"], file_ids except Exception as e: # You will get a SlackApiError if "ok" is False error_str = self._enrich_error(str(e), channel) self.log.warning("Error happened: {}".format(error_str)) - def _python3_call(self, token, channel, message, - publish_files): + def _python3_call(self, token, channel, message, publish_files): from slack_sdk import WebClient from slack_sdk.errors import SlackApiError try: client = WebClient(token=token) attachment_str = "\n\n Attachment links: \n" + file_ids = [] for published_file in publish_files: response = client.files_upload( file=published_file, @@ -176,16 +195,16 @@ def _python3_call(self, token, channel, message, attachment_str += "\n<{}|{}>".format( response["file"]["permalink"], os.path.basename(published_file)) + file_ids.append(response["file"]["id"]) if publish_files: message += attachment_str - _ = client.chat_postMessage( + response = client.chat_postMessage( channel=channel, - text=message, - username=self.bot_user_name, - icon_url=self.icon_url + text=message ) + return response.data["ts"], file_ids except SlackApiError as e: # You will get a SlackApiError if "ok" is False error_str = self._enrich_error(str(e.response["error"]), channel) From 5e8f0e0152df9547caed72d4b401a5fb9200fb30 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 12 Jan 2022 17:02:58 +0100 Subject: [PATCH 466/492] OP-1730 - removed obsolete variables Modification username and icon via message payload doesn't work for both post_method and upload_file. Icon must be set in Slack app configuration. --- openpype/modules/slack/plugins/publish/integrate_slack_api.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/openpype/modules/slack/plugins/publish/integrate_slack_api.py b/openpype/modules/slack/plugins/publish/integrate_slack_api.py index 6afbbf58497..5d014382a35 100644 --- a/openpype/modules/slack/plugins/publish/integrate_slack_api.py +++ b/openpype/modules/slack/plugins/publish/integrate_slack_api.py @@ -26,10 +26,6 @@ class IntegrateSlackAPI(pyblish.api.InstancePlugin): optional = True - # internal, not configurable - bot_user_name = "OpenPypeNotifier" - icon_url = "https://openpype.io/img/favicon/favicon.ico" - def process(self, instance): thumbnail_path = self._get_thumbnail_path(instance) review_path = self._get_review_path(instance) From 87c5eb549786fa2e166dd381fe4b97204b146b48 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 12 Jan 2022 17:55:22 +0100 Subject: [PATCH 467/492] Expose toggle publish plug-in settings for Maya Look Shading Engine Naming --- openpype/settings/defaults/project_settings/maya.json | 5 +++++ .../projects_schema/schemas/schema_maya_publish.json | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index b75b0168ece..a7560711061 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -166,6 +166,11 @@ "enabled": false, "regex": "(?P.*)_(.*)_SHD" }, + "ValidateShadingEngine": { + "enabled": true, + "optional": true, + "active": true + }, "ValidateAttributes": { "enabled": false, "attributes": {} diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json index 606dd6c2bb3..7c9a5a6b460 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json @@ -72,6 +72,17 @@ ] }, + { + "type": "schema_template", + "name": "template_publish_plugin", + "template_data": [ + { + "key": "ValidateShadingEngine", + "label": "Validate Look Shading Engine Naming" + } + ] + }, + { "type": "dict", "collapsible": true, From 6df5488ccec9f9621b5fdb00e68791c0fcc7781e Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Wed, 12 Jan 2022 18:07:31 +0100 Subject: [PATCH 468/492] fix type --- igniter/bootstrap_repos.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/igniter/bootstrap_repos.py b/igniter/bootstrap_repos.py index db62cbbe914..637f8213661 100644 --- a/igniter/bootstrap_repos.py +++ b/igniter/bootstrap_repos.py @@ -762,7 +762,7 @@ def create_version_from_live_code( destination = self._move_zip_to_data_dir(temp_zip) - return OpenPypeVersion(version=version, path=destination) + return OpenPypeVersion(version=version, path=Path(destination)) def _move_zip_to_data_dir(self, zip_file) -> Union[None, Path]: """Move zip with OpenPype version to user data directory. From 91930038880fb24c68518f5d135b5eba44bdbf05 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Wed, 12 Jan 2022 18:46:44 +0100 Subject: [PATCH 469/492] fix dir/file resolution --- tools/create_zip.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/create_zip.py b/tools/create_zip.py index 32a4d27e8b0..2fc351469a8 100644 --- a/tools/create_zip.py +++ b/tools/create_zip.py @@ -31,7 +31,9 @@ def progress(inc: int): bs = bootstrap_repos.BootstrapRepos(progress_callback=progress) if path: out_path = Path(path) - bs.data_dir = out_path.parent + bs.data_dir = out_path + if out_path.is_file(): + bs.data_dir = out_path.parent _print(f"Creating zip in {bs.data_dir} ...") repo_file = bs.create_version_from_live_code() From 5d9ddca7d0aef5c84d0119fac0396fb2790f3f1c Mon Sep 17 00:00:00 2001 From: "IGOTGAMES\\jesse.d" Date: Wed, 12 Jan 2022 17:25:22 -0800 Subject: [PATCH 470/492] Fixed bug: File list would be 1 file long if node frame range is 2 frames long. --- openpype/hosts/houdini/plugins/publish/collect_frames.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/houdini/plugins/publish/collect_frames.py b/openpype/hosts/houdini/plugins/publish/collect_frames.py index ef77c3230bc..8d21794c1bc 100644 --- a/openpype/hosts/houdini/plugins/publish/collect_frames.py +++ b/openpype/hosts/houdini/plugins/publish/collect_frames.py @@ -37,7 +37,7 @@ def process(self, instance): # Check if frames are bigger than 1 (file collection) # override the result - if end_frame - start_frame > 1: + if end_frame - start_frame > 0: result = self.create_file_list( match, int(start_frame), int(end_frame) ) From 9d9f9514c1bbc25046b16a6c882505b428fa3c61 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 13 Jan 2022 11:02:21 +0100 Subject: [PATCH 471/492] format output arguments with anatomy data --- openpype/plugins/publish/extract_review.py | 25 +++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index b6c2e49385c..be29c7bf9c1 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -187,6 +187,7 @@ def main_process(self, instance): outputs_per_repres = self._get_outputs_per_representations( instance, profile_outputs ) + fill_data = copy.deepcopy(instance.data["anatomyData"]) for repre, outputs in outputs_per_repres: # Check if input should be preconverted before processing # Store original staging dir (it's value may change) @@ -293,7 +294,7 @@ def main_process(self, instance): try: # temporary until oiiotool is supported cross platform ffmpeg_args = self._ffmpeg_arguments( - output_def, instance, new_repre, temp_data + output_def, instance, new_repre, temp_data, fill_data ) except ZeroDivisionError: if 'exr' in temp_data["origin_repre"]["ext"]: @@ -446,7 +447,9 @@ def prepare_temp_data(self, instance, repre, output_def): "handles_are_set": handles_are_set } - def _ffmpeg_arguments(self, output_def, instance, new_repre, temp_data): + def _ffmpeg_arguments( + self, output_def, instance, new_repre, temp_data, fill_data + ): """Prepares ffmpeg arguments for expected extraction. Prepares input and output arguments based on output definition and @@ -472,9 +475,6 @@ def _ffmpeg_arguments(self, output_def, instance, new_repre, temp_data): ffmpeg_input_args = [ value for value in _ffmpeg_input_args if value.strip() ] - ffmpeg_output_args = [ - value for value in _ffmpeg_output_args if value.strip() - ] ffmpeg_video_filters = [ value for value in _ffmpeg_video_filters if value.strip() ] @@ -482,6 +482,21 @@ def _ffmpeg_arguments(self, output_def, instance, new_repre, temp_data): value for value in _ffmpeg_audio_filters if value.strip() ] + ffmpeg_output_args = [] + for value in _ffmpeg_output_args: + value = value.strip() + if not value: + continue + try: + value = value.format(**fill_data) + except Exception: + self.log.warning( + "Failed to format ffmpeg argument: {}".format(value), + exc_info=True + ) + pass + ffmpeg_output_args.append(value) + # Prepare input and output filepaths self.input_output_paths(new_repre, output_def, temp_data) From 29445314346e644ea02e1df8f4479ce8b587a32d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 13 Jan 2022 13:02:51 +0100 Subject: [PATCH 472/492] implemented callback warpper for execution in main thread --- openpype/tools/tray/pype_tray.py | 24 ++++++----- openpype/tools/utils/__init__.py | 5 ++- openpype/tools/utils/lib.py | 70 +++++++++++++++++++++++++++++++- 3 files changed, 87 insertions(+), 12 deletions(-) diff --git a/openpype/tools/tray/pype_tray.py b/openpype/tools/tray/pype_tray.py index df0238c8485..e7ac390c304 100644 --- a/openpype/tools/tray/pype_tray.py +++ b/openpype/tools/tray/pype_tray.py @@ -22,6 +22,7 @@ ProjectSettings, DefaultsNotDefined ) +from openpype.tools.utils import WrappedCallbackItem from .pype_info_widget import PypeInfoWidget @@ -61,21 +62,24 @@ def execute_doubleclick(self): if callback: self.execute_in_main_thread(callback) - def execute_in_main_thread(self, callback): - self._main_thread_callbacks.append(callback) + def execute_in_main_thread(self, callback, *args, **kwargs): + if isinstance(callback, WrappedCallbackItem): + item = callback + else: + item = WrappedCallbackItem(callback, *args, **kwargs) + + self._main_thread_callbacks.append(item) + + return item def _main_thread_execution(self): if self._execution_in_progress: return self._execution_in_progress = True - while self._main_thread_callbacks: - try: - callback = self._main_thread_callbacks.popleft() - callback() - except: - self.log.warning( - "Failed to execute {} in main thread".format(callback), - exc_info=True) + for _ in range(len(self._main_thread_callbacks)): + if self._main_thread_callbacks: + item = self._main_thread_callbacks.popleft() + item.execute() self._execution_in_progress = False diff --git a/openpype/tools/utils/__init__.py b/openpype/tools/utils/__init__.py index 4dd6bdd05f2..65025ac358e 100644 --- a/openpype/tools/utils/__init__.py +++ b/openpype/tools/utils/__init__.py @@ -6,6 +6,7 @@ ) from .error_dialog import ErrorMessageBox +from .lib import WrappedCallbackItem __all__ = ( @@ -14,5 +15,7 @@ "ClickableFrame", "ExpandBtn", - "ErrorMessageBox" + "ErrorMessageBox", + + "WrappedCallbackItem", ) diff --git a/openpype/tools/utils/lib.py b/openpype/tools/utils/lib.py index 6742df85572..5f3456ae3e5 100644 --- a/openpype/tools/utils/lib.py +++ b/openpype/tools/utils/lib.py @@ -9,7 +9,10 @@ from avalon import style from avalon.vendor import qtawesome -from openpype.api import get_project_settings +from openpype.api import ( + get_project_settings, + Logger +) from openpype.lib import filter_profiles @@ -598,3 +601,68 @@ def is_remove_site_loader(loader): def is_add_site_loader(loader): return hasattr(loader, "add_site_to_representation") + + +class WrappedCallbackItem: + """Structure to store information about callback and args/kwargs for it. + + Item can be used to execute callback in main thread which may be needed + for execution of Qt objects. + + Item store callback (callable variable), arguments and keyword arguments + for the callback. Item hold information about it's process. + """ + not_set = object() + _log = None + + def __init__(self, callback, *args, **kwargs): + self._done = False + self._exception = self.not_set + self._result = self.not_set + self._callback = callback + self._args = args + self._kwargs = kwargs + + def __call__(self): + self.execute() + + @property + def log(self): + cls = self.__class__ + if cls._log is None: + cls._log = Logger.get_logger(cls.__name__) + return cls._log + + @property + def done(self): + return self._done + + @property + def exception(self): + return self._exception + + @property + def result(self): + return self._result + + def execute(self): + """Execute callback and store it's result. + + Method must be called from main thread. Item is marked as `done` + when callback execution finished. Store output of callback of exception + information when callback raise one. + """ + if self.done: + self.log.warning("- item is already processed") + return + + self.log.debug("Running callback: {}".format(str(self._callback))) + try: + result = self._callback(*self._args, **self._kwargs) + self._result = result + + except Exception as exc: + self._exception = exc + + finally: + self._done = True From d7fb171f101bf36495a106fbca296515f692b080 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 13 Jan 2022 13:08:35 +0100 Subject: [PATCH 473/492] added check if current running openpype has expected version --- openpype/lib/__init__.py | 6 ++- openpype/lib/pype_info.py | 48 +++++++++++++++++---- openpype/tools/tray/pype_tray.py | 74 ++++++++++++++++++++++++++++++-- 3 files changed, 115 insertions(+), 13 deletions(-) diff --git a/openpype/lib/__init__.py b/openpype/lib/__init__.py index 12e47a8961b..65019f3fab5 100644 --- a/openpype/lib/__init__.py +++ b/openpype/lib/__init__.py @@ -170,7 +170,9 @@ from .pype_info import ( get_openpype_version, - get_build_version + get_build_version, + is_running_from_build, + is_current_version_studio_latest ) terminal = Terminal @@ -304,4 +306,6 @@ "get_openpype_version", "get_build_version", + "is_running_from_build", + "is_current_version_studio_latest", ] diff --git a/openpype/lib/pype_info.py b/openpype/lib/pype_info.py index 15856bfb19d..ea804c8a181 100644 --- a/openpype/lib/pype_info.py +++ b/openpype/lib/pype_info.py @@ -10,6 +10,12 @@ from .execute import get_openpype_execute_args from .local_settings import get_local_site_id from .python_module_tools import import_filepath +from .openpype_version import ( + op_version_control_available, + openpype_path_is_accessible, + get_expected_studio_version, + get_OpenPypeVersion +) def get_openpype_version(): @@ -17,15 +23,6 @@ def get_openpype_version(): return openpype.version.__version__ -def get_pype_version(): - """Backwards compatibility. Remove when 100% not used.""" - print(( - "Using deprecated function 'openpype.lib.pype_info.get_pype_version'" - " replace with 'openpype.lib.pype_info.get_openpype_version'." - )) - return get_openpype_version() - - def get_build_version(): """OpenPype version of build.""" # Return OpenPype version if is running from code @@ -138,3 +135,36 @@ def extract_pype_info_to_file(dirpath): with open(filepath, "w") as file_stream: json.dump(data, file_stream, indent=4) return filepath + + +def is_current_version_studio_latest(): + """Is currently running OpenPype version which is defined by studio. + + It is not recommended to ask in each process as there may be situations + when older OpenPype should be used. For example on farm. But it does make + sense in processes that can run for a long time. + + Returns: + None: Can't determine. e.g. when running from code or the build is + too old. + bool: True when is using studio + """ + output = None + # Skip if is not running from build + if not is_running_from_build(): + return output + + # Skip if build does not support version control + if not op_version_control_available(): + return output + + # Skip if path to folder with zip files is not accessible + if not openpype_path_is_accessible(): + return output + + # Check if current version is expected version + OpenPypeVersion = get_OpenPypeVersion() + current_version = OpenPypeVersion(get_openpype_version()) + expected_version = get_expected_studio_version(is_running_staging()) + + return current_version == expected_version diff --git a/openpype/tools/tray/pype_tray.py b/openpype/tools/tray/pype_tray.py index e7ac390c304..5af82b2c641 100644 --- a/openpype/tools/tray/pype_tray.py +++ b/openpype/tools/tray/pype_tray.py @@ -14,7 +14,11 @@ resources, get_system_settings ) -from openpype.lib import get_openpype_execute_args +from openpype.lib import ( + get_openpype_execute_args, + is_current_version_studio_latest, + is_running_from_build +) from openpype.modules import TrayModulesManager from openpype import style from openpype.settings import ( @@ -27,11 +31,43 @@ from .pype_info_widget import PypeInfoWidget +class VersionDialog(QtWidgets.QDialog): + def __init__(self, parent=None): + super(VersionDialog, self).__init__(parent) + + label_widget = QtWidgets.QLabel( + "Your version does not match to studio version", self + ) + + ignore_btn = QtWidgets.QPushButton("Ignore", self) + restart_btn = QtWidgets.QPushButton("Restart and Install", self) + + btns_layout = QtWidgets.QHBoxLayout() + btns_layout.addStretch(1) + btns_layout.addWidget(ignore_btn, 0) + btns_layout.addWidget(restart_btn, 0) + + layout = QtWidgets.QVBoxLayout(self) + layout.addWidget(label_widget, 0) + layout.addStretch(1) + layout.addLayout(btns_layout, 0) + + ignore_btn.clicked.connect(self._on_ignore) + restart_btn.clicked.connect(self._on_reset) + + def _on_ignore(self): + self.reject() + + def _on_reset(self): + self.accept() + + class TrayManager: """Cares about context of application. Load submenus, actions, separators and modules into tray's context. """ + _version_check_interval = 5 * 60 * 1000 def __init__(self, tray_widget, main_window): self.tray_widget = tray_widget @@ -46,6 +82,9 @@ def __init__(self, tray_widget, main_window): self.errors = [] + self._version_check_timer = None + self._version_dialog = None + self.main_thread_timer = None self._main_thread_callbacks = collections.deque() self._execution_in_progress = None @@ -62,6 +101,24 @@ def execute_doubleclick(self): if callback: self.execute_in_main_thread(callback) + def _on_version_check_timer(self): + # Check if is running from build and stop future validations if yes + if not is_running_from_build(): + self._version_check_timer.stop() + return + + self.validate_openpype_version() + + def validate_openpype_version(self): + if is_current_version_studio_latest(): + return + + if self._version_dialog is None: + self._version_dialog = VersionDialog() + result = self._version_dialog.exec_() + if result: + self.restart() + def execute_in_main_thread(self, callback, *args, **kwargs): if isinstance(callback, WrappedCallbackItem): item = callback @@ -123,6 +180,12 @@ def initialize_modules(self): self.main_thread_timer = main_thread_timer + version_check_timer = QtCore.QTimer() + version_check_timer.setInterval(self._version_check_interval) + version_check_timer.timeout.connect(self._on_version_check_timer) + version_check_timer.start() + self._version_check_timer = version_check_timer + # For storing missing settings dialog self._settings_validation_dialog = None @@ -207,7 +270,7 @@ def _add_version_item(self): self.tray_widget.menu.addAction(version_action) self.tray_widget.menu.addSeparator() - def restart(self): + def restart(self, reset_version=True): """Restart Tray tool. First creates new process with same argument and close current tray. @@ -221,7 +284,9 @@ def restart(self): additional_args.pop(0) args.extend(additional_args) - kwargs = {} + kwargs = { + "env": dict(os.environ.items()) + } if platform.system().lower() == "windows": flags = ( subprocess.CREATE_NEW_PROCESS_GROUP @@ -229,6 +294,9 @@ def restart(self): ) kwargs["creationflags"] = flags + if reset_version: + kwargs["env"].pop("OPENPYPE_VERSION", None) + subprocess.Popen(args, **kwargs) self.exit() From 7d283f55558f203cd98265ec327be6e2caa5fd53 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 13 Jan 2022 17:08:37 +0100 Subject: [PATCH 474/492] moved code from pype_info to openpype_version and fixed few bugs --- openpype/lib/__init__.py | 2 +- openpype/lib/openpype_version.py | 117 ++++++++++++++++++++++++++++++- openpype/lib/pype_info.py | 89 +---------------------- openpype/resources/__init__.py | 2 +- 4 files changed, 118 insertions(+), 92 deletions(-) diff --git a/openpype/lib/__init__.py b/openpype/lib/__init__.py index 65019f3fab5..c556f2adc14 100644 --- a/openpype/lib/__init__.py +++ b/openpype/lib/__init__.py @@ -168,7 +168,7 @@ make_sequence_collection ) -from .pype_info import ( +from .openpype_version import ( get_openpype_version, get_build_version, is_running_from_build, diff --git a/openpype/lib/openpype_version.py b/openpype/lib/openpype_version.py index e3a4e1fa3e8..839222018c4 100644 --- a/openpype/lib/openpype_version.py +++ b/openpype/lib/openpype_version.py @@ -9,9 +9,69 @@ repository or locally available. """ +import os import sys +import openpype.version +from .python_module_tools import import_filepath + + +# ---------------------------------------- +# Functions independent on OpenPypeVersion +# ---------------------------------------- +def get_openpype_version(): + """Version of pype that is currently used.""" + return openpype.version.__version__ + + +def get_build_version(): + """OpenPype version of build.""" + # Return OpenPype version if is running from code + if not is_running_from_build(): + return get_openpype_version() + + # Import `version.py` from build directory + version_filepath = os.path.join( + os.environ["OPENPYPE_ROOT"], + "openpype", + "version.py" + ) + if not os.path.exists(version_filepath): + return None + + module = import_filepath(version_filepath, "openpype_build_version") + return getattr(module, "__version__", None) + + +def is_running_from_build(): + """Determine if current process is running from build or code. + + Returns: + bool: True if running from build. + """ + executable_path = os.environ["OPENPYPE_EXECUTABLE"] + executable_filename = os.path.basename(executable_path) + if "python" in executable_filename.lower(): + return False + return True + + +def is_running_staging(): + """Currently used OpenPype is staging version. + + Returns: + bool: True if openpype version containt 'staging'. + """ + if "staging" in get_openpype_version(): + return True + return False + + +# ---------------------------------------- +# Functions dependent on OpenPypeVersion +# - Make sense to call only in OpenPype process +# ---------------------------------------- def get_OpenPypeVersion(): """Access to OpenPypeVersion class stored in sys modules.""" return sys.modules.get("OpenPypeVersion") @@ -71,15 +131,66 @@ def get_remote_versions(*args, **kwargs): return None -def get_latest_version(*args, **kwargs): +def get_latest_version(staging=None, local=None, remote=None): """Get latest version from repository path.""" + if staging is None: + staging = is_running_staging() if op_version_control_available(): - return get_OpenPypeVersion().get_latest_version(*args, **kwargs) + return get_OpenPypeVersion().get_latest_version( + staging=staging, + local=local, + remote=remote + ) return None -def get_expected_studio_version(staging=False): +def get_expected_studio_version(staging=None): """Expected production or staging version in studio.""" + if staging is None: + staging = is_running_staging() if op_version_control_available(): return get_OpenPypeVersion().get_expected_studio_version(staging) return None + + +def is_current_version_studio_latest(): + """Is currently running OpenPype version which is defined by studio. + + It is not recommended to ask in each process as there may be situations + when older OpenPype should be used. For example on farm. But it does make + sense in processes that can run for a long time. + + Returns: + None: Can't determine. e.g. when running from code or the build is + too old. + bool: True when is using studio + """ + output = None + # Skip if is not running from build + if not is_running_from_build(): + return output + + # Skip if build does not support version control + if not op_version_control_available(): + return output + + # Skip if path to folder with zip files is not accessible + if not openpype_path_is_accessible(): + return output + + # Get OpenPypeVersion class + OpenPypeVersion = get_OpenPypeVersion() + # Convert current version to OpenPypeVersion object + current_version = OpenPypeVersion(version=get_openpype_version()) + + staging = is_running_staging() + # Get expected version (from settings) + expected_version = get_expected_studio_version(staging) + if expected_version is None: + # Look for latest if expected version is not set in settings + expected_version = get_latest_version( + staging=staging, + remote=True + ) + # Check if current version is expected version + return current_version == expected_version diff --git a/openpype/lib/pype_info.py b/openpype/lib/pype_info.py index ea804c8a181..848a505187e 100644 --- a/openpype/lib/pype_info.py +++ b/openpype/lib/pype_info.py @@ -5,67 +5,15 @@ import getpass import socket -import openpype.version from openpype.settings.lib import get_local_settings from .execute import get_openpype_execute_args from .local_settings import get_local_site_id -from .python_module_tools import import_filepath from .openpype_version import ( - op_version_control_available, - openpype_path_is_accessible, - get_expected_studio_version, - get_OpenPypeVersion + is_running_from_build, + get_openpype_version ) -def get_openpype_version(): - """Version of pype that is currently used.""" - return openpype.version.__version__ - - -def get_build_version(): - """OpenPype version of build.""" - # Return OpenPype version if is running from code - if not is_running_from_build(): - return get_openpype_version() - - # Import `version.py` from build directory - version_filepath = os.path.join( - os.environ["OPENPYPE_ROOT"], - "openpype", - "version.py" - ) - if not os.path.exists(version_filepath): - return None - - module = import_filepath(version_filepath, "openpype_build_version") - return getattr(module, "__version__", None) - - -def is_running_from_build(): - """Determine if current process is running from build or code. - - Returns: - bool: True if running from build. - """ - executable_path = os.environ["OPENPYPE_EXECUTABLE"] - executable_filename = os.path.basename(executable_path) - if "python" in executable_filename.lower(): - return False - return True - - -def is_running_staging(): - """Currently used OpenPype is staging version. - - Returns: - bool: True if openpype version containt 'staging'. - """ - if "staging" in get_openpype_version(): - return True - return False - - def get_pype_info(): """Information about currently used Pype process.""" executable_args = get_openpype_execute_args() @@ -135,36 +83,3 @@ def extract_pype_info_to_file(dirpath): with open(filepath, "w") as file_stream: json.dump(data, file_stream, indent=4) return filepath - - -def is_current_version_studio_latest(): - """Is currently running OpenPype version which is defined by studio. - - It is not recommended to ask in each process as there may be situations - when older OpenPype should be used. For example on farm. But it does make - sense in processes that can run for a long time. - - Returns: - None: Can't determine. e.g. when running from code or the build is - too old. - bool: True when is using studio - """ - output = None - # Skip if is not running from build - if not is_running_from_build(): - return output - - # Skip if build does not support version control - if not op_version_control_available(): - return output - - # Skip if path to folder with zip files is not accessible - if not openpype_path_is_accessible(): - return output - - # Check if current version is expected version - OpenPypeVersion = get_OpenPypeVersion() - current_version = OpenPypeVersion(get_openpype_version()) - expected_version = get_expected_studio_version(is_running_staging()) - - return current_version == expected_version diff --git a/openpype/resources/__init__.py b/openpype/resources/__init__.py index f4639335253..34a833d080f 100644 --- a/openpype/resources/__init__.py +++ b/openpype/resources/__init__.py @@ -1,5 +1,5 @@ import os -from openpype.lib.pype_info import is_running_staging +from openpype.lib.openpype_version import is_running_staging RESOURCES_DIR = os.path.dirname(os.path.abspath(__file__)) From 12156b6d90f723d6a96016fb51c3e876415dca8c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 13 Jan 2022 17:57:27 +0100 Subject: [PATCH 475/492] tray will show info that is outdated and user should restart --- openpype/lib/__init__.py | 2 + openpype/lib/openpype_version.py | 37 ++++++------ openpype/style/data.json | 6 +- openpype/style/style.css | 8 +-- .../project_manager/project_manager/style.py | 2 +- .../project_manager/widgets.py | 2 +- openpype/tools/tray/pype_tray.py | 58 ++++++++++++++++--- 7 files changed, 81 insertions(+), 34 deletions(-) diff --git a/openpype/lib/__init__.py b/openpype/lib/__init__.py index c556f2adc14..a2a16bcc004 100644 --- a/openpype/lib/__init__.py +++ b/openpype/lib/__init__.py @@ -171,6 +171,7 @@ from .openpype_version import ( get_openpype_version, get_build_version, + get_expected_version, is_running_from_build, is_current_version_studio_latest ) @@ -306,6 +307,7 @@ "get_openpype_version", "get_build_version", + "get_expected_version", "is_running_from_build", "is_current_version_studio_latest", ] diff --git a/openpype/lib/openpype_version.py b/openpype/lib/openpype_version.py index 839222018c4..201bf646e95 100644 --- a/openpype/lib/openpype_version.py +++ b/openpype/lib/openpype_version.py @@ -153,6 +153,17 @@ def get_expected_studio_version(staging=None): return None +def get_expected_version(staging=None): + expected_version = get_expected_studio_version(staging) + if expected_version is None: + # Look for latest if expected version is not set in settings + expected_version = get_latest_version( + staging=staging, + remote=True + ) + return expected_version + + def is_current_version_studio_latest(): """Is currently running OpenPype version which is defined by studio. @@ -166,16 +177,13 @@ def is_current_version_studio_latest(): bool: True when is using studio """ output = None - # Skip if is not running from build - if not is_running_from_build(): - return output - - # Skip if build does not support version control - if not op_version_control_available(): - return output - - # Skip if path to folder with zip files is not accessible - if not openpype_path_is_accessible(): + # Skip if is not running from build or build does not support version + # control or path to folder with zip files is not accessible + if ( + not is_running_from_build() + or not op_version_control_available() + or not openpype_path_is_accessible() + ): return output # Get OpenPypeVersion class @@ -183,14 +191,7 @@ def is_current_version_studio_latest(): # Convert current version to OpenPypeVersion object current_version = OpenPypeVersion(version=get_openpype_version()) - staging = is_running_staging() # Get expected version (from settings) - expected_version = get_expected_studio_version(staging) - if expected_version is None: - # Look for latest if expected version is not set in settings - expected_version = get_latest_version( - staging=staging, - remote=True - ) + expected_version = get_expected_version() # Check if current version is expected version return current_version == expected_version diff --git a/openpype/style/data.json b/openpype/style/data.json index b3dffd7c712..6e1b6e822b9 100644 --- a/openpype/style/data.json +++ b/openpype/style/data.json @@ -51,8 +51,10 @@ "border-hover": "rgba(168, 175, 189, .3)", "border-focus": "rgb(92, 173, 214)", - "delete-btn-bg": "rgb(201, 54, 54)", - "delete-btn-bg-disabled": "rgba(201, 54, 54, 64)", + "warning-btn-bg": "rgb(201, 54, 54)", + + "warning-btn-bg": "rgb(201, 54, 54)", + "warning-btn-bg-disabled": "rgba(201, 54, 54, 64)", "tab-widget": { "bg": "#21252B", diff --git a/openpype/style/style.css b/openpype/style/style.css index 7f7f30e2bcd..65e8d0cb406 100644 --- a/openpype/style/style.css +++ b/openpype/style/style.css @@ -734,11 +734,11 @@ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { background: {color:bg-view-hover}; } -#DeleteButton { - background: {color:delete-btn-bg}; +#WarningButton { + background: {color:warning-btn-bg}; } -#DeleteButton:disabled { - background: {color:delete-btn-bg-disabled}; +#WarningButton:disabled { + background: {color:warning-btn-bg-disabled}; } /* Launcher specific stylesheets */ diff --git a/openpype/tools/project_manager/project_manager/style.py b/openpype/tools/project_manager/project_manager/style.py index 9fa7a5520bc..980c637bcaf 100644 --- a/openpype/tools/project_manager/project_manager/style.py +++ b/openpype/tools/project_manager/project_manager/style.py @@ -95,7 +95,7 @@ def get_remove_icon(cls): def get_warning_pixmap(cls): src_image = get_warning_image() colors = get_objected_colors() - color_value = colors["delete-btn-bg"] + color_value = colors["warning-btn-bg"] return paint_image_with_color( src_image, diff --git a/openpype/tools/project_manager/project_manager/widgets.py b/openpype/tools/project_manager/project_manager/widgets.py index 4b5aca35ef7..e58dcc7d0cf 100644 --- a/openpype/tools/project_manager/project_manager/widgets.py +++ b/openpype/tools/project_manager/project_manager/widgets.py @@ -369,7 +369,7 @@ def __init__(self, project_name, parent): cancel_btn = QtWidgets.QPushButton("Cancel", self) cancel_btn.setToolTip("Cancel deletion of the project") confirm_btn = QtWidgets.QPushButton("Permanently Delete Project", self) - confirm_btn.setObjectName("DeleteButton") + confirm_btn.setObjectName("WarningButton") confirm_btn.setEnabled(False) confirm_btn.setToolTip("Confirm deletion") diff --git a/openpype/tools/tray/pype_tray.py b/openpype/tools/tray/pype_tray.py index 5af82b2c641..c32cf17e186 100644 --- a/openpype/tools/tray/pype_tray.py +++ b/openpype/tools/tray/pype_tray.py @@ -17,7 +17,9 @@ from openpype.lib import ( get_openpype_execute_args, is_current_version_studio_latest, - is_running_from_build + is_running_from_build, + get_expected_version, + get_openpype_version ) from openpype.modules import TrayModulesManager from openpype import style @@ -32,15 +34,30 @@ class VersionDialog(QtWidgets.QDialog): + restart_requested = QtCore.Signal() + + _min_width = 400 + _min_height = 130 + def __init__(self, parent=None): super(VersionDialog, self).__init__(parent) - - label_widget = QtWidgets.QLabel( - "Your version does not match to studio version", self + self.setWindowTitle("Wrong OpenPype version") + icon = QtGui.QIcon(resources.get_openpype_icon_filepath()) + self.setWindowIcon(icon) + self.setWindowFlags( + self.windowFlags() + | QtCore.Qt.WindowStaysOnTopHint ) + self.setMinimumWidth(self._min_width) + self.setMinimumHeight(self._min_height) + + label_widget = QtWidgets.QLabel(self) + label_widget.setWordWrap(True) + ignore_btn = QtWidgets.QPushButton("Ignore", self) - restart_btn = QtWidgets.QPushButton("Restart and Install", self) + ignore_btn.setObjectName("WarningButton") + restart_btn = QtWidgets.QPushButton("Restart and Change", self) btns_layout = QtWidgets.QHBoxLayout() btns_layout.addStretch(1) @@ -55,10 +72,22 @@ def __init__(self, parent=None): ignore_btn.clicked.connect(self._on_ignore) restart_btn.clicked.connect(self._on_reset) + self._label_widget = label_widget + + self.setStyleSheet(style.load_stylesheet()) + + def update_versions(self, current_version, expected_version): + message = ( + "Your OpenPype version {} does" + " not match to studio version {}" + ).format(str(current_version), str(expected_version)) + self._label_widget.setText(message) + def _on_ignore(self): self.reject() def _on_reset(self): + self.restart_requested.emit() self.accept() @@ -115,9 +144,22 @@ def validate_openpype_version(self): if self._version_dialog is None: self._version_dialog = VersionDialog() - result = self._version_dialog.exec_() - if result: - self.restart() + self._version_dialog.restart_requested.connect( + self._restart_and_install + ) + + if self._version_dialog.isVisible(): + return + + expected_version = get_expected_version() + current_version = get_openpype_version() + self._version_dialog.update_versions( + current_version, expected_version + ) + self._version_dialog.exec_() + + def _restart_and_install(self): + self.restart() def execute_in_main_thread(self, callback, *args, **kwargs): if isinstance(callback, WrappedCallbackItem): From 644711c9d61f34e83b5f821d833c597b032a37b5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 13 Jan 2022 18:16:23 +0100 Subject: [PATCH 476/492] status action gives information about openpype version --- .../ftrack/scripts/sub_event_status.py | 57 ++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/openpype/modules/default_modules/ftrack/scripts/sub_event_status.py b/openpype/modules/default_modules/ftrack/scripts/sub_event_status.py index 004f61338c6..3163642e3fe 100644 --- a/openpype/modules/default_modules/ftrack/scripts/sub_event_status.py +++ b/openpype/modules/default_modules/ftrack/scripts/sub_event_status.py @@ -16,8 +16,14 @@ TOPIC_STATUS_SERVER_RESULT ) from openpype.api import Logger +from openpype.lib import ( + is_current_version_studio_latest, + is_running_from_build, + get_expected_version, + get_openpype_version +) -log = Logger().get_logger("Event storer") +log = Logger.get_logger("Event storer") action_identifier = ( "event.server.status" + os.environ["FTRACK_EVENT_SUB_ID"] ) @@ -203,8 +209,57 @@ def bool_items(self): }) return items + def openpype_version_items(self): + items = [] + is_latest = is_current_version_studio_latest() + items.append({ + "type": "label", + "value": "# OpenPype version" + }) + if not is_running_from_build(): + items.append({ + "type": "label", + "value": ( + "OpenPype event server is running from code {}." + ).format(str(get_openpype_version())) + }) + + elif is_latest is None: + items.append({ + "type": "label", + "value": ( + "Can't determine if OpenPype version is outdated" + " {}. OpenPype build version should be updated." + ).format(str(get_openpype_version())) + }) + elif is_latest: + items.append({ + "type": "label", + "value": "OpenPype version is up to date {}.".format( + str(get_openpype_version()) + ) + }) + else: + items.append({ + "type": "label", + "value": ( + "Using outdated OpenPype version {}." + " Expected version is {}." + "
- Please restart event server for automatic" + " updates or update manually." + ).format( + str(get_openpype_version()), + str(get_expected_version()) + ) + }) + + items.append({"type": "label", "value": "---"}) + + return items + def items(self): items = [] + items.extend(self.openpype_version_items()) items.append(self.note_item) items.extend(self.bool_items()) From 4ee86a6ce27f0a56b88926719c61bab308e5c144 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 13 Jan 2022 18:26:13 +0100 Subject: [PATCH 477/492] show tray message when update dialog is ignored --- openpype/tools/tray/pype_tray.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/openpype/tools/tray/pype_tray.py b/openpype/tools/tray/pype_tray.py index c32cf17e186..17251b404fc 100644 --- a/openpype/tools/tray/pype_tray.py +++ b/openpype/tools/tray/pype_tray.py @@ -35,6 +35,7 @@ class VersionDialog(QtWidgets.QDialog): restart_requested = QtCore.Signal() + ignore_requested = QtCore.Signal() _min_width = 400 _min_height = 130 @@ -73,9 +74,19 @@ def __init__(self, parent=None): restart_btn.clicked.connect(self._on_reset) self._label_widget = label_widget + self._restart_accepted = False self.setStyleSheet(style.load_stylesheet()) + def showEvent(self, event): + super().showEvent(event) + self._restart_accepted = False + + def closeEvent(self, event): + super().closeEvent(event) + if not self._restart_accepted: + self.ignore_requested.emit() + def update_versions(self, current_version, expected_version): message = ( "Your OpenPype version {} does" @@ -87,6 +98,7 @@ def _on_ignore(self): self.reject() def _on_reset(self): + self._restart_accepted = True self.restart_requested.emit() self.accept() @@ -147,6 +159,9 @@ def validate_openpype_version(self): self._version_dialog.restart_requested.connect( self._restart_and_install ) + self._version_dialog.ignore_requested.connect( + self._outdated_version_ignored + ) if self._version_dialog.isVisible(): return @@ -161,6 +176,15 @@ def validate_openpype_version(self): def _restart_and_install(self): self.restart() + def _outdated_version_ignored(self): + self.show_tray_message( + "Outdated OpenPype version", + ( + "Please update your OpenPype as soon as possible." + " All you have to do is to restart tray." + ) + ) + def execute_in_main_thread(self, callback, *args, **kwargs): if isinstance(callback, WrappedCallbackItem): item = callback From 687181e3825373894111f8a6267ad9fd9fe99917 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 14 Jan 2022 10:58:36 +0100 Subject: [PATCH 478/492] interval of validation can be modified --- .../defaults/system_settings/general.json | 1 + .../schemas/system_schema/schema_general.json | 13 +++++++++++++ openpype/tools/tray/pype_tray.py | 17 ++++++++++++----- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/openpype/settings/defaults/system_settings/general.json b/openpype/settings/defaults/system_settings/general.json index a07152eaf87..7c78de9a5c8 100644 --- a/openpype/settings/defaults/system_settings/general.json +++ b/openpype/settings/defaults/system_settings/general.json @@ -4,6 +4,7 @@ "admin_password": "", "production_version": "", "staging_version": "", + "version_check_interval": 5, "environment": { "__environment_keys__": { "global": [] diff --git a/openpype/settings/entities/schemas/system_schema/schema_general.json b/openpype/settings/entities/schemas/system_schema/schema_general.json index b4c83fc85f0..3af3f5ce354 100644 --- a/openpype/settings/entities/schemas/system_schema/schema_general.json +++ b/openpype/settings/entities/schemas/system_schema/schema_general.json @@ -47,6 +47,19 @@ { "type": "splitter" }, + { + "type": "label", + "label": "Trigger validation if running OpenPype is using studio defined version each 'n' minutes. Validation happens in OpenPype tray application." + }, + { + "type": "number", + "key": "version_check_interval", + "label": "Version check interval", + "minimum": 0 + }, + { + "type": "splitter" + }, { "key": "environment", "label": "Environment", diff --git a/openpype/tools/tray/pype_tray.py b/openpype/tools/tray/pype_tray.py index 17251b404fc..de1a8577b05 100644 --- a/openpype/tools/tray/pype_tray.py +++ b/openpype/tools/tray/pype_tray.py @@ -108,8 +108,6 @@ class TrayManager: Load submenus, actions, separators and modules into tray's context. """ - _version_check_interval = 5 * 60 * 1000 - def __init__(self, tray_widget, main_window): self.tray_widget = tray_widget self.main_window = main_window @@ -117,7 +115,15 @@ def __init__(self, tray_widget, main_window): self.log = Logger.get_logger(self.__class__.__name__) - self.module_settings = get_system_settings()["modules"] + system_settings = get_system_settings() + self.module_settings = system_settings["modules"] + + version_check_interval = system_settings["general"].get( + "version_check_interval" + ) + if version_check_interval is None: + version_check_interval = 5 + self._version_check_interval = version_check_interval * 60 * 1000 self.modules_manager = TrayModulesManager() @@ -247,9 +253,10 @@ def initialize_modules(self): self.main_thread_timer = main_thread_timer version_check_timer = QtCore.QTimer() - version_check_timer.setInterval(self._version_check_interval) version_check_timer.timeout.connect(self._on_version_check_timer) - version_check_timer.start() + if self._version_check_interval > 0: + version_check_timer.setInterval(self._version_check_interval) + version_check_timer.start() self._version_check_timer = version_check_timer # For storing missing settings dialog From 0ebd7881c144a16e98a8923c4f5e2f8ea22a355e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 14 Jan 2022 11:40:38 +0100 Subject: [PATCH 479/492] fixed reseting from staging --- openpype/tools/tray/pype_tray.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/openpype/tools/tray/pype_tray.py b/openpype/tools/tray/pype_tray.py index de1a8577b05..7f781402111 100644 --- a/openpype/tools/tray/pype_tray.py +++ b/openpype/tools/tray/pype_tray.py @@ -18,6 +18,7 @@ get_openpype_execute_args, is_current_version_studio_latest, is_running_from_build, + is_running_staging, get_expected_version, get_openpype_version ) @@ -349,17 +350,25 @@ def restart(self, reset_version=True): First creates new process with same argument and close current tray. """ args = get_openpype_execute_args() + kwargs = { + "env": dict(os.environ.items()) + } + # Create a copy of sys.argv additional_args = list(sys.argv) # Check last argument from `get_openpype_execute_args` # - when running from code it is the same as first from sys.argv if args[-1] == additional_args[0]: additional_args.pop(0) - args.extend(additional_args) - kwargs = { - "env": dict(os.environ.items()) - } + # Pop OPENPYPE_VERSION + if reset_version: + # Add staging flag if was running from staging + if is_running_staging(): + args.append("--use-staging") + kwargs["env"].pop("OPENPYPE_VERSION", None) + + args.extend(additional_args) if platform.system().lower() == "windows": flags = ( subprocess.CREATE_NEW_PROCESS_GROUP @@ -367,9 +376,6 @@ def restart(self, reset_version=True): ) kwargs["creationflags"] = flags - if reset_version: - kwargs["env"].pop("OPENPYPE_VERSION", None) - subprocess.Popen(args, **kwargs) self.exit() From 438d6df439cbec5ebe9da311dbad4a6cda7144d2 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 14 Jan 2022 15:56:36 +0100 Subject: [PATCH 480/492] burnins fix bit rate for dnxhd mxf passing metadata to burnins --- openpype/scripts/otio_burnin.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/openpype/scripts/otio_burnin.py b/openpype/scripts/otio_burnin.py index 3fc1412e62f..639657d68fc 100644 --- a/openpype/scripts/otio_burnin.py +++ b/openpype/scripts/otio_burnin.py @@ -157,6 +157,16 @@ def _dnxhd_codec_args(stream_data, source_ffmpeg_cmd): if pix_fmt: output.extend(["-pix_fmt", pix_fmt]) + # Use arguments from source if are available source arguments + if source_ffmpeg_cmd: + copy_args = ( + "-b:v", "-vb", + ) + args = source_ffmpeg_cmd.split(" ") + for idx, arg in enumerate(args): + if arg in copy_args: + output.extend([arg, args[idx + 1]]) + output.extend(["-g", "1"]) return output @@ -716,6 +726,15 @@ def burnins_from_data( ffmpeg_args.extend( get_codec_args(burnin.ffprobe_data, source_ffmpeg_cmd) ) + # Use arguments from source if are available source arguments + if source_ffmpeg_cmd: + copy_args = ( + "-metadata", + ) + args = source_ffmpeg_cmd.split(" ") + for idx, arg in enumerate(args): + if arg in copy_args: + ffmpeg_args.extend([arg, args[idx + 1]]) # Use group one (same as `-intra` argument, which is deprecated) ffmpeg_args_str = " ".join(ffmpeg_args) From 17578c54471ad931bc48afc82b5aa8ccd4a29908 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 14 Jan 2022 16:20:05 +0100 Subject: [PATCH 481/492] fix import if 'is_running_staging' --- openpype/lib/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/lib/__init__.py b/openpype/lib/__init__.py index a2a16bcc004..62d204186de 100644 --- a/openpype/lib/__init__.py +++ b/openpype/lib/__init__.py @@ -173,6 +173,7 @@ get_build_version, get_expected_version, is_running_from_build, + is_running_staging, is_current_version_studio_latest ) @@ -309,5 +310,6 @@ "get_build_version", "get_expected_version", "is_running_from_build", + "is_running_staging", "is_current_version_studio_latest", ] From ce5c70e28d99d1528ab12e75be91c1a219b38aae Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 14 Jan 2022 17:47:11 +0100 Subject: [PATCH 482/492] change back project manager styles --- openpype/style/data.json | 5 ++--- openpype/style/style.css | 13 +++++++++---- .../tools/project_manager/project_manager/style.py | 2 +- .../project_manager/project_manager/widgets.py | 2 +- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/openpype/style/data.json b/openpype/style/data.json index 6e1b6e822b9..c8adc0674a4 100644 --- a/openpype/style/data.json +++ b/openpype/style/data.json @@ -51,10 +51,9 @@ "border-hover": "rgba(168, 175, 189, .3)", "border-focus": "rgb(92, 173, 214)", - "warning-btn-bg": "rgb(201, 54, 54)", - "warning-btn-bg": "rgb(201, 54, 54)", - "warning-btn-bg-disabled": "rgba(201, 54, 54, 64)", + "delete-btn-bg": "rgb(201, 54, 54)", + "delete-btn-bg-disabled": "rgba(201, 54, 54, 64)", "tab-widget": { "bg": "#21252B", diff --git a/openpype/style/style.css b/openpype/style/style.css index 65e8d0cb406..d9b0ff74218 100644 --- a/openpype/style/style.css +++ b/openpype/style/style.css @@ -734,11 +734,11 @@ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { background: {color:bg-view-hover}; } -#WarningButton { - background: {color:warning-btn-bg}; +#DeleteButton { + background: {color:delete-btn-bg}; } -#WarningButton:disabled { - background: {color:warning-btn-bg-disabled}; +#DeleteButton:disabled { + background: {color:delete-btn-bg-disabled}; } /* Launcher specific stylesheets */ @@ -1228,6 +1228,11 @@ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { background: #21252B; } +/* Tray */ +#TrayRestartButton { + background: {color:restart-btn-bg}; +} + /* Globally used names */ #Separator { background: {color:bg-menu-separator}; diff --git a/openpype/tools/project_manager/project_manager/style.py b/openpype/tools/project_manager/project_manager/style.py index 980c637bcaf..9fa7a5520bc 100644 --- a/openpype/tools/project_manager/project_manager/style.py +++ b/openpype/tools/project_manager/project_manager/style.py @@ -95,7 +95,7 @@ def get_remove_icon(cls): def get_warning_pixmap(cls): src_image = get_warning_image() colors = get_objected_colors() - color_value = colors["warning-btn-bg"] + color_value = colors["delete-btn-bg"] return paint_image_with_color( src_image, diff --git a/openpype/tools/project_manager/project_manager/widgets.py b/openpype/tools/project_manager/project_manager/widgets.py index e58dcc7d0cf..4b5aca35ef7 100644 --- a/openpype/tools/project_manager/project_manager/widgets.py +++ b/openpype/tools/project_manager/project_manager/widgets.py @@ -369,7 +369,7 @@ def __init__(self, project_name, parent): cancel_btn = QtWidgets.QPushButton("Cancel", self) cancel_btn.setToolTip("Cancel deletion of the project") confirm_btn = QtWidgets.QPushButton("Permanently Delete Project", self) - confirm_btn.setObjectName("WarningButton") + confirm_btn.setObjectName("DeleteButton") confirm_btn.setEnabled(False) confirm_btn.setToolTip("Confirm deletion") From a18bdbc418e004bb8d3c0606296d1649872fa74b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 14 Jan 2022 17:53:56 +0100 Subject: [PATCH 483/492] changed dialog and added restart and update action to tray --- openpype/style/data.json | 1 + openpype/tools/tray/images/gifts.png | Bin 0 -> 8605 bytes openpype/tools/tray/pype_tray.py | 118 +++++++++++++++++++++++---- openpype/tools/utils/__init__.py | 6 +- 4 files changed, 108 insertions(+), 17 deletions(-) create mode 100644 openpype/tools/tray/images/gifts.png diff --git a/openpype/style/data.json b/openpype/style/data.json index c8adc0674a4..1db0c732cf2 100644 --- a/openpype/style/data.json +++ b/openpype/style/data.json @@ -51,6 +51,7 @@ "border-hover": "rgba(168, 175, 189, .3)", "border-focus": "rgb(92, 173, 214)", + "restart-btn-bg": "#458056", "delete-btn-bg": "rgb(201, 54, 54)", "delete-btn-bg-disabled": "rgba(201, 54, 54, 64)", diff --git a/openpype/tools/tray/images/gifts.png b/openpype/tools/tray/images/gifts.png new file mode 100644 index 0000000000000000000000000000000000000000..57fb3f286312878c641f27362653ede1b7746810 GIT binary patch literal 8605 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 T%VmTC_`iqu)s>1NPv86(+fBse literal 0 HcmV?d00001 diff --git a/openpype/tools/tray/pype_tray.py b/openpype/tools/tray/pype_tray.py index 7f781402111..0d3e7ae04ca 100644 --- a/openpype/tools/tray/pype_tray.py +++ b/openpype/tools/tray/pype_tray.py @@ -29,11 +29,51 @@ ProjectSettings, DefaultsNotDefined ) -from openpype.tools.utils import WrappedCallbackItem +from openpype.tools.utils import ( + WrappedCallbackItem, + paint_image_with_color +) from .pype_info_widget import PypeInfoWidget +# TODO PixmapLabel should be moved to 'utils' in other future PR so should be +# imported from there +class PixmapLabel(QtWidgets.QLabel): + """Label resizing image to height of font.""" + def __init__(self, pixmap, parent): + super(PixmapLabel, self).__init__(parent) + self._empty_pixmap = QtGui.QPixmap(0, 0) + self._source_pixmap = pixmap + + def set_source_pixmap(self, pixmap): + """Change source image.""" + self._source_pixmap = pixmap + self._set_resized_pix() + + def _get_pix_size(self): + size = self.fontMetrics().height() * 3 + return size, size + + def _set_resized_pix(self): + if self._source_pixmap is None: + self.setPixmap(self._empty_pixmap) + return + width, height = self._get_pix_size() + self.setPixmap( + self._source_pixmap.scaled( + width, + height, + QtCore.Qt.KeepAspectRatio, + QtCore.Qt.SmoothTransformation + ) + ) + + def resizeEvent(self, event): + self._set_resized_pix() + super(PixmapLabel, self).resizeEvent(event) + + class VersionDialog(QtWidgets.QDialog): restart_requested = QtCore.Signal() ignore_requested = QtCore.Signal() @@ -43,7 +83,7 @@ class VersionDialog(QtWidgets.QDialog): def __init__(self, parent=None): super(VersionDialog, self).__init__(parent) - self.setWindowTitle("Wrong OpenPype version") + self.setWindowTitle("OpenPype update is needed") icon = QtGui.QIcon(resources.get_openpype_icon_filepath()) self.setWindowIcon(icon) self.setWindowFlags( @@ -54,12 +94,23 @@ def __init__(self, parent=None): self.setMinimumWidth(self._min_width) self.setMinimumHeight(self._min_height) - label_widget = QtWidgets.QLabel(self) + top_widget = QtWidgets.QWidget(self) + + gift_pixmap = self._get_gift_pixmap() + gift_icon_label = PixmapLabel(gift_pixmap, top_widget) + + label_widget = QtWidgets.QLabel(top_widget) label_widget.setWordWrap(True) - ignore_btn = QtWidgets.QPushButton("Ignore", self) - ignore_btn.setObjectName("WarningButton") - restart_btn = QtWidgets.QPushButton("Restart and Change", self) + top_layout = QtWidgets.QHBoxLayout(top_widget) + # top_layout.setContentsMargins(0, 0, 0, 0) + top_layout.setSpacing(10) + top_layout.addWidget(gift_icon_label, 0, QtCore.Qt.AlignCenter) + top_layout.addWidget(label_widget, 1) + + ignore_btn = QtWidgets.QPushButton("Later", self) + restart_btn = QtWidgets.QPushButton("Restart && Update", self) + restart_btn.setObjectName("TrayRestartButton") btns_layout = QtWidgets.QHBoxLayout() btns_layout.addStretch(1) @@ -67,7 +118,7 @@ def __init__(self, parent=None): btns_layout.addWidget(restart_btn, 0) layout = QtWidgets.QVBoxLayout(self) - layout.addWidget(label_widget, 0) + layout.addWidget(top_widget, 0) layout.addStretch(1) layout.addLayout(btns_layout, 0) @@ -79,6 +130,21 @@ def __init__(self, parent=None): self.setStyleSheet(style.load_stylesheet()) + def _get_gift_pixmap(self): + image_path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "images", + "gifts.png" + ) + src_image = QtGui.QImage(image_path) + colors = style.get_objected_colors() + color_value = colors["font"] + + return paint_image_with_color( + src_image, + color_value.get_qcolor() + ) + def showEvent(self, event): super().showEvent(event) self._restart_accepted = False @@ -90,8 +156,8 @@ def closeEvent(self, event): def update_versions(self, current_version, expected_version): message = ( - "Your OpenPype version {} does" - " not match to studio version {}" + "Running OpenPype version is {}." + " Your production has been updated to version {}." ).format(str(current_version), str(expected_version)) self._label_widget.setText(message) @@ -113,6 +179,7 @@ def __init__(self, tray_widget, main_window): self.tray_widget = tray_widget self.main_window = main_window self.pype_info_widget = None + self._restart_action = None self.log = Logger.get_logger(self.__class__.__name__) @@ -158,7 +225,14 @@ def _on_version_check_timer(self): self.validate_openpype_version() def validate_openpype_version(self): - if is_current_version_studio_latest(): + using_requested = is_current_version_studio_latest() + self._restart_action.setVisible(not using_requested) + if using_requested: + if ( + self._version_dialog is not None + and self._version_dialog.isVisible() + ): + self._version_dialog.close() return if self._version_dialog is None: @@ -170,25 +244,24 @@ def validate_openpype_version(self): self._outdated_version_ignored ) - if self._version_dialog.isVisible(): - return - expected_version = get_expected_version() current_version = get_openpype_version() self._version_dialog.update_versions( current_version, expected_version ) - self._version_dialog.exec_() + self._version_dialog.show() + self._version_dialog.raise_() + self._version_dialog.activateWindow() def _restart_and_install(self): self.restart() def _outdated_version_ignored(self): self.show_tray_message( - "Outdated OpenPype version", + "OpenPype version is outdated", ( "Please update your OpenPype as soon as possible." - " All you have to do is to restart tray." + " To update, restart OpenPype Tray application." ) ) @@ -341,9 +414,22 @@ def _add_version_item(self): version_action = QtWidgets.QAction(version_string, self.tray_widget) version_action.triggered.connect(self._on_version_action) + + restart_action = QtWidgets.QAction( + "Restart && Update", self.tray_widget + ) + restart_action.triggered.connect(self._on_restart_action) + restart_action.setVisible(False) + self.tray_widget.menu.addAction(version_action) + self.tray_widget.menu.addAction(restart_action) self.tray_widget.menu.addSeparator() + self._restart_action = restart_action + + def _on_restart_action(self): + self.restart() + def restart(self, reset_version=True): """Restart Tray tool. diff --git a/openpype/tools/utils/__init__.py b/openpype/tools/utils/__init__.py index 65025ac358e..eb0cb1eef50 100644 --- a/openpype/tools/utils/__init__.py +++ b/openpype/tools/utils/__init__.py @@ -6,7 +6,10 @@ ) from .error_dialog import ErrorMessageBox -from .lib import WrappedCallbackItem +from .lib import ( + WrappedCallbackItem, + paint_image_with_color +) __all__ = ( @@ -18,4 +21,5 @@ "ErrorMessageBox", "WrappedCallbackItem", + "paint_image_with_color", ) From 3495ed1b06be855a9551fb6312815ce79cadcd18 Mon Sep 17 00:00:00 2001 From: OpenPype Date: Sat, 15 Jan 2022 03:44:00 +0000 Subject: [PATCH 484/492] [Automated] Bump version --- CHANGELOG.md | 20 ++++++++++++++------ openpype/version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e92c16dc5f5..e7cd3cb7d2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,18 +1,26 @@ # Changelog -## [3.8.0-nightly.3](https://github.com/pypeclub/OpenPype/tree/HEAD) +## [3.8.0-nightly.4](https://github.com/pypeclub/OpenPype/tree/HEAD) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.7.0...HEAD) +### 📖 Documentation + +- Slack: Add review to notification message [\#2498](https://github.com/pypeclub/OpenPype/pull/2498) + **🆕 New features** - Flame: OpenTimelineIO Export Modul [\#2398](https://github.com/pypeclub/OpenPype/pull/2398) **🚀 Enhancements** +- General: Be able to use anatomy data in ffmpeg output arguments [\#2525](https://github.com/pypeclub/OpenPype/pull/2525) +- Expose toggle publish plug-in settings for Maya Look Shading Engine Naming [\#2521](https://github.com/pypeclub/OpenPype/pull/2521) - Photoshop: Move implementation to OpenPype [\#2510](https://github.com/pypeclub/OpenPype/pull/2510) - TimersManager: Move module one hierarchy higher [\#2501](https://github.com/pypeclub/OpenPype/pull/2501) +- Slack: notifications are sent with Openpype logo and bot name [\#2499](https://github.com/pypeclub/OpenPype/pull/2499) - Ftrack: Event handlers settings [\#2496](https://github.com/pypeclub/OpenPype/pull/2496) +- Flame - create publishable clips [\#2495](https://github.com/pypeclub/OpenPype/pull/2495) - Tools: Fix style and modality of errors in loader and creator [\#2489](https://github.com/pypeclub/OpenPype/pull/2489) - Project Manager: Remove project button cleanup [\#2482](https://github.com/pypeclub/OpenPype/pull/2482) - Tools: Be able to change models of tasks and assets widgets [\#2475](https://github.com/pypeclub/OpenPype/pull/2475) @@ -23,23 +31,27 @@ - Fix \#2453 Refactor missing \_get\_reference\_node method [\#2455](https://github.com/pypeclub/OpenPype/pull/2455) - Houdini: Remove broken unique name counter [\#2450](https://github.com/pypeclub/OpenPype/pull/2450) - Maya: Improve lib.polyConstraint performance when Select tool is not the active tool context [\#2447](https://github.com/pypeclub/OpenPype/pull/2447) +- General: Validate third party before build [\#2425](https://github.com/pypeclub/OpenPype/pull/2425) - Maya : add option to not group reference in ReferenceLoader [\#2383](https://github.com/pypeclub/OpenPype/pull/2383) **🐛 Bug fixes** +- Fix published frame content for sequence starting with 0 [\#2513](https://github.com/pypeclub/OpenPype/pull/2513) +- Fix \#2497: reset empty string attributes correctly to "" instead of "None" [\#2506](https://github.com/pypeclub/OpenPype/pull/2506) - General: Settings work if OpenPypeVersion is available [\#2494](https://github.com/pypeclub/OpenPype/pull/2494) - General: PYTHONPATH may break OpenPype dependencies [\#2493](https://github.com/pypeclub/OpenPype/pull/2493) - Workfiles tool: Files widget show files on first show [\#2488](https://github.com/pypeclub/OpenPype/pull/2488) - General: Custom template paths filter fix [\#2483](https://github.com/pypeclub/OpenPype/pull/2483) - Loader: Remove always on top flag in tray [\#2480](https://github.com/pypeclub/OpenPype/pull/2480) - General: Anatomy does not return root envs as unicode [\#2465](https://github.com/pypeclub/OpenPype/pull/2465) +- Maya: Validate Shape Zero do not keep fixed geometry vertices selected/active after repair [\#2456](https://github.com/pypeclub/OpenPype/pull/2456) **Merged pull requests:** +- Fix create zip tool - path argument [\#2522](https://github.com/pypeclub/OpenPype/pull/2522) - General: Modules import function output fix [\#2492](https://github.com/pypeclub/OpenPype/pull/2492) - AE: fix hiding of alert window below Publish [\#2491](https://github.com/pypeclub/OpenPype/pull/2491) - Maya: Validate NGONs re-use polyConstraint code from openpype.host.maya.api.lib [\#2458](https://github.com/pypeclub/OpenPype/pull/2458) -- Version handling [\#2363](https://github.com/pypeclub/OpenPype/pull/2363) ## [3.7.0](https://github.com/pypeclub/OpenPype/tree/3.7.0) (2022-01-04) @@ -68,7 +80,6 @@ - Blender 3: Support auto install for new blender version [\#2377](https://github.com/pypeclub/OpenPype/pull/2377) - Maya add render image path to settings [\#2375](https://github.com/pypeclub/OpenPype/pull/2375) - Hiero: python3 compatibility [\#2365](https://github.com/pypeclub/OpenPype/pull/2365) -- Maya: Add is\_static\_image\_plane and is\_in\_all\_views option in imagePlaneLoader [\#2356](https://github.com/pypeclub/OpenPype/pull/2356) **🐛 Bug fixes** @@ -87,8 +98,6 @@ - Nuke: fixing menu re-drawing during context change [\#2374](https://github.com/pypeclub/OpenPype/pull/2374) - Webpublisher: Fix assignment of families of TVpaint instances [\#2373](https://github.com/pypeclub/OpenPype/pull/2373) - Nuke: fixing node name based on switched asset name [\#2369](https://github.com/pypeclub/OpenPype/pull/2369) -- Tools: Placeholder color [\#2359](https://github.com/pypeclub/OpenPype/pull/2359) -- Houdini: Fix HDA creation [\#2350](https://github.com/pypeclub/OpenPype/pull/2350) **Merged pull requests:** @@ -96,7 +105,6 @@ - Maya: Replaced PATH usage with vendored oiio path for maketx utility [\#2405](https://github.com/pypeclub/OpenPype/pull/2405) - \[Fix\]\[MAYA\] Handle message type attribute within CollectLook [\#2394](https://github.com/pypeclub/OpenPype/pull/2394) - Add validator to check correct version of extension for PS and AE [\#2387](https://github.com/pypeclub/OpenPype/pull/2387) -- Linux : flip updating submodules logic [\#2357](https://github.com/pypeclub/OpenPype/pull/2357) ## [3.6.4](https://github.com/pypeclub/OpenPype/tree/3.6.4) (2021-11-23) diff --git a/openpype/version.py b/openpype/version.py index 1f005d69522..520048bca7f 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.8.0-nightly.3" +__version__ = "3.8.0-nightly.4" diff --git a/pyproject.toml b/pyproject.toml index f9155f05a37..598d2b47989 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.8.0-nightly.3" # OpenPype +version = "3.8.0-nightly.4" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From b432613e726770d4f21360f8db65ff3936af8429 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Sun, 16 Jan 2022 16:13:10 +0100 Subject: [PATCH 485/492] moved implementation from avalon to openpype --- openpype/hosts/aftereffects/api/README.md | 66 + openpype/hosts/aftereffects/api/__init__.py | 181 +-- openpype/hosts/aftereffects/api/extension.zxp | Bin 0 -> 100915 bytes .../hosts/aftereffects/api/extension/.debug | 32 + .../api/extension/CSXS/manifest.xml | 79 ++ .../api/extension/css/boilerplate.css | 327 +++++ .../aftereffects/api/extension/css/styles.css | 51 + .../css/topcoat-desktop-dark.min.css | 1 + .../api/extension/icons/iconDarkNormal.png | Bin 0 -> 18659 bytes .../api/extension/icons/iconDarkRollover.png | Bin 0 -> 18663 bytes .../api/extension/icons/iconDisabled.png | Bin 0 -> 18663 bytes .../api/extension/icons/iconNormal.png | Bin 0 -> 18225 bytes .../api/extension/icons/iconRollover.png | Bin 0 -> 18664 bytes .../aftereffects/api/extension/index.html | 136 ++ .../api/extension/js/libs/CSInterface.js | 1193 +++++++++++++++++ .../api/extension/js/libs/jquery-2.0.2.min.js | 6 + .../api/extension/js/libs/json.js | 530 ++++++++ .../api/extension/js/libs/loglevel.min.js | 2 + .../api/extension/js/libs/wsrpc.js | 393 ++++++ .../api/extension/js/libs/wsrpc.min.js | 1 + .../aftereffects/api/extension/js/main.js | 347 +++++ .../api/extension/js/themeManager.js | 128 ++ .../api/extension/jsx/hostscript.jsx | 723 ++++++++++ .../hosts/aftereffects/api/launch_logic.py | 319 +++++ openpype/hosts/aftereffects/api/lib.py | 71 + openpype/hosts/aftereffects/api/panel.PNG | Bin 0 -> 8756 bytes .../hosts/aftereffects/api/panel_failure.PNG | Bin 0 -> 13568 bytes openpype/hosts/aftereffects/api/pipeline.py | 272 ++++ openpype/hosts/aftereffects/api/plugin.py | 46 + openpype/hosts/aftereffects/api/workio.py | 49 + openpype/hosts/aftereffects/api/ws_stub.py | 605 +++++++++ .../plugins/create/create_local_render.py | 4 - .../plugins/create/create_render.py | 42 +- .../plugins/load/load_background.py | 22 +- .../aftereffects/plugins/load/load_file.py | 19 +- .../plugins/publish/add_publish_highlight.py | 4 +- .../aftereffects/plugins/publish/closeAE.py | 4 +- .../plugins/publish/collect_audio.py | 5 +- .../plugins/publish/collect_current_file.py | 4 +- .../publish/collect_extension_version.py | 10 +- .../plugins/publish/collect_render.py | 6 +- .../plugins/publish/extract_local_render.py | 9 +- .../plugins/publish/extract_save_scene.py | 4 +- .../plugins/publish/increment_workfile.py | 4 +- .../publish/remove_publish_highlight.py | 4 +- .../publish/validate_instance_asset.py | 4 +- .../publish/validate_scene_settings.py | 10 +- openpype/scripts/non_python_host_launch.py | 2 +- 48 files changed, 5530 insertions(+), 185 deletions(-) create mode 100644 openpype/hosts/aftereffects/api/README.md create mode 100644 openpype/hosts/aftereffects/api/extension.zxp create mode 100644 openpype/hosts/aftereffects/api/extension/.debug create mode 100644 openpype/hosts/aftereffects/api/extension/CSXS/manifest.xml create mode 100644 openpype/hosts/aftereffects/api/extension/css/boilerplate.css create mode 100644 openpype/hosts/aftereffects/api/extension/css/styles.css create mode 100644 openpype/hosts/aftereffects/api/extension/css/topcoat-desktop-dark.min.css create mode 100644 openpype/hosts/aftereffects/api/extension/icons/iconDarkNormal.png create mode 100644 openpype/hosts/aftereffects/api/extension/icons/iconDarkRollover.png create mode 100644 openpype/hosts/aftereffects/api/extension/icons/iconDisabled.png create mode 100644 openpype/hosts/aftereffects/api/extension/icons/iconNormal.png create mode 100644 openpype/hosts/aftereffects/api/extension/icons/iconRollover.png create mode 100644 openpype/hosts/aftereffects/api/extension/index.html create mode 100644 openpype/hosts/aftereffects/api/extension/js/libs/CSInterface.js create mode 100644 openpype/hosts/aftereffects/api/extension/js/libs/jquery-2.0.2.min.js create mode 100644 openpype/hosts/aftereffects/api/extension/js/libs/json.js create mode 100644 openpype/hosts/aftereffects/api/extension/js/libs/loglevel.min.js create mode 100644 openpype/hosts/aftereffects/api/extension/js/libs/wsrpc.js create mode 100644 openpype/hosts/aftereffects/api/extension/js/libs/wsrpc.min.js create mode 100644 openpype/hosts/aftereffects/api/extension/js/main.js create mode 100644 openpype/hosts/aftereffects/api/extension/js/themeManager.js create mode 100644 openpype/hosts/aftereffects/api/extension/jsx/hostscript.jsx create mode 100644 openpype/hosts/aftereffects/api/launch_logic.py create mode 100644 openpype/hosts/aftereffects/api/lib.py create mode 100644 openpype/hosts/aftereffects/api/panel.PNG create mode 100644 openpype/hosts/aftereffects/api/panel_failure.PNG create mode 100644 openpype/hosts/aftereffects/api/pipeline.py create mode 100644 openpype/hosts/aftereffects/api/plugin.py create mode 100644 openpype/hosts/aftereffects/api/workio.py create mode 100644 openpype/hosts/aftereffects/api/ws_stub.py diff --git a/openpype/hosts/aftereffects/api/README.md b/openpype/hosts/aftereffects/api/README.md new file mode 100644 index 00000000000..667324f7a47 --- /dev/null +++ b/openpype/hosts/aftereffects/api/README.md @@ -0,0 +1,66 @@ +# Photoshop Integration + +Requirements: This extension requires use of Javascript engine, which is +available since CC 16.0. +Please check your File>Project Settings>Expressions>Expressions Engine + +## Setup + +The After Effects integration requires two components to work; `extension` and `server`. + +### Extension + +To install the extension download [Extension Manager Command Line tool (ExManCmd)](https://github.com/Adobe-CEP/Getting-Started-guides/tree/master/Package%20Distribute%20Install#option-2---exmancmd). + +``` +ExManCmd /install {path to avalon-core}\avalon\photoshop\extension.zxp +``` +OR +download [Anastasiy’s Extension Manager](https://install.anastasiy.com/) + +### Server + +The easiest way to get the server and After Effects launch is with: + +``` +python -c ^"import avalon.photoshop;avalon.aftereffects.launch(""c:\Program Files\Adobe\Adobe After Effects 2020\Support Files\AfterFX.exe"")^" +``` + +`avalon.aftereffects.launch` launches the application and server, and also closes the server when After Effects exists. + +## Usage + +The After Effects extension can be found under `Window > Extensions > OpenPype`. Once launched you should be presented with a panel like this: + +![Avalon Panel](panel.PNG "Avalon Panel") + + +## Developing + +### Extension +When developing the extension you can load it [unsigned](https://github.com/Adobe-CEP/CEP-Resources/blob/master/CEP_9.x/Documentation/CEP%209.0%20HTML%20Extension%20Cookbook.md#debugging-unsigned-extensions). + +When signing the extension you can use this [guide](https://github.com/Adobe-CEP/Getting-Started-guides/tree/master/Package%20Distribute%20Install#package-distribute-install-guide). + +``` +ZXPSignCmd -selfSignedCert NA NA Avalon Avalon-After-Effects avalon extension.p12 +ZXPSignCmd -sign {path to avalon-core}\avalon\aftereffects\extension {path to avalon-core}\avalon\aftereffects\extension.zxp extension.p12 avalon +``` + +### Plugin Examples + +These plugins were made with the [polly config](https://github.com/mindbender-studio/config). To fully integrate and load, you will have to use this config and add `image` to the [integration plugin](https://github.com/mindbender-studio/config/blob/master/polly/plugins/publish/integrate_asset.py). + +Expected deployed extension location on default Windows: +`c:\Program Files (x86)\Common Files\Adobe\CEP\extensions\com.openpype.AE.panel` + +For easier debugging of Javascript: +https://community.adobe.com/t5/download-install/adobe-extension-debuger-problem/td-p/10911704?page=1 +Add (optional) --enable-blink-features=ShadowDOMV0,CustomElementsV0 when starting Chrome +then localhost:8092 + +Or use Visual Studio Code https://medium.com/adobetech/extendscript-debugger-for-visual-studio-code-public-release-a2ff6161fa01 +## Resources + - https://javascript-tools-guide.readthedocs.io/introduction/index.html + - https://github.com/Adobe-CEP/Getting-Started-guides + - https://github.com/Adobe-CEP/CEP-Resources diff --git a/openpype/hosts/aftereffects/api/__init__.py b/openpype/hosts/aftereffects/api/__init__.py index b1edb91a5ca..a7bbd8e604a 100644 --- a/openpype/hosts/aftereffects/api/__init__.py +++ b/openpype/hosts/aftereffects/api/__init__.py @@ -1,115 +1,66 @@ -import os -import sys -import logging - -from avalon import io -from avalon import api as avalon -from Qt import QtWidgets -from openpype import lib, api -import pyblish.api as pyblish -import openpype.hosts.aftereffects - - -log = logging.getLogger("openpype.hosts.aftereffects") - - -HOST_DIR = os.path.dirname(os.path.abspath(openpype.hosts.aftereffects.__file__)) -PLUGINS_DIR = os.path.join(HOST_DIR, "plugins") -PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish") -LOAD_PATH = os.path.join(PLUGINS_DIR, "load") -CREATE_PATH = os.path.join(PLUGINS_DIR, "create") -INVENTORY_PATH = os.path.join(PLUGINS_DIR, "inventory") - - -def check_inventory(): - if not lib.any_outdated(): - return - - host = pyblish.registered_host() - outdated_containers = [] - for container in host.ls(): - representation = container['representation'] - representation_doc = io.find_one( - { - "_id": io.ObjectId(representation), - "type": "representation" - }, - projection={"parent": True} - ) - if representation_doc and not lib.is_latest(representation_doc): - outdated_containers.append(container) - - # Warn about outdated containers. - print("Starting new QApplication..") - app = QtWidgets.QApplication(sys.argv) - - message_box = QtWidgets.QMessageBox() - message_box.setIcon(QtWidgets.QMessageBox.Warning) - msg = "There are outdated containers in the scene." - message_box.setText(msg) - message_box.exec_() - - # Garbage collect QApplication. - del app - - -def application_launch(): - check_inventory() - - -def install(): - print("Installing Pype config...") - - pyblish.register_plugin_path(PUBLISH_PATH) - avalon.register_plugin_path(avalon.Loader, LOAD_PATH) - avalon.register_plugin_path(avalon.Creator, CREATE_PATH) - log.info(PUBLISH_PATH) - - pyblish.register_callback( - "instanceToggled", on_pyblish_instance_toggled - ) - - avalon.on("application.launched", application_launch) - - -def uninstall(): - pyblish.deregister_plugin_path(PUBLISH_PATH) - avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH) - avalon.deregister_plugin_path(avalon.Creator, CREATE_PATH) - - -def on_pyblish_instance_toggled(instance, old_value, new_value): - """Toggle layer visibility on instance toggles.""" - instance[0].Visible = new_value - - -def get_asset_settings(): - """Get settings on current asset from database. - - Returns: - dict: Scene data. - - """ - asset_data = lib.get_asset()["data"] - fps = asset_data.get("fps") - frame_start = asset_data.get("frameStart") - frame_end = asset_data.get("frameEnd") - handle_start = asset_data.get("handleStart") - handle_end = asset_data.get("handleEnd") - resolution_width = asset_data.get("resolutionWidth") - resolution_height = asset_data.get("resolutionHeight") - duration = (frame_end - frame_start + 1) + handle_start + handle_end - entity_type = asset_data.get("entityType") - - scene_data = { - "fps": fps, - "frameStart": frame_start, - "frameEnd": frame_end, - "handleStart": handle_start, - "handleEnd": handle_end, - "resolutionWidth": resolution_width, - "resolutionHeight": resolution_height, - "duration": duration - } - - return scene_data +"""Public API + +Anything that isn't defined here is INTERNAL and unreliable for external use. + +""" + +from .launch_logic import ( + get_stub, + stub, +) + +from .pipeline import ( + ls, + Creator, + install, + list_instances, + remove_instance, + containerise +) + +from .workio import ( + file_extensions, + has_unsaved_changes, + save_file, + open_file, + current_file, + work_root, +) + +from .lib import ( + maintained_selection, + get_extension_manifest_path +) + +from .plugin import ( + AfterEffectsLoader +) + + +__all__ = [ + # launch_logic + "get_stub", + "stub", + + # pipeline + "ls", + "Creator", + "install", + "list_instances", + "remove_instance", + "containerise", + + "file_extensions", + "has_unsaved_changes", + "save_file", + "open_file", + "current_file", + "work_root", + + # lib + "maintained_selection", + "get_extension_manifest_path", + + # plugin + "AfterEffectsLoader", +] diff --git a/openpype/hosts/aftereffects/api/extension.zxp b/openpype/hosts/aftereffects/api/extension.zxp new file mode 100644 index 0000000000000000000000000000000000000000..35b0c0fc42a731150521187312ef21243882f300 GIT binary patch literal 100915 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

CHAj%#mo5y_a_ zLCD5kyarp-^Ne?2dtSD0F&-avX>!& z0}>whiUTSq&|8zkWv}Kq4XTthZM%jvlCT>l+(N~keT<*NI;bNuxD8@q z(G-haZIK*y66PF7GZWr-l<>y~EKSdc^NE1Png=Gwq-f*+)Pzb*hzA`d43H;H(0~>p zRQg?uO|1D>8_lCbeh7=i6$T%K_1q9m31T#A*I)bw`uklTX5=(o`F{2VtSNHR(zFYj^cWmD|aG;{VHs%fi4%jhly*Y%j(73W$Efvfe;a#EF-CW&$U zq2kcysq{QbmRrT--&C>=0*?U;HV;Wrk%vo+BYtTod7p3RJG@-AnM%GA1 z?{x%JElo)gnLOd(rd@XJA(VEgmDk+7&s)xm#=F4_%_~K$EuyRo=RG6K(WTPb+v@Zc zu|wKjrYERHv@mvO-QfK(yf{>Rnl?ScSqgJWzxk`H4YrvYOBthOYvKIQO#16_vaO@- z;nZX;M{7#UXzfni?OgfUx8GZ3kou>w&jB)0gCT1?>=PR0z0iFWn$LYIN;Q3z1z%S| z2;{10m*k~+gMz(*j+Wb<0ofr@q=h}*ng^HET>C)BK#Khg-Pif^^}+8rOjFUG z-jsw+5w)Qj1?S@P<&G1)Jgv~sExQjE4RcYos=2zvTgI*YLIRat5bGXsDSsyK zk+I-mJZknctp$bwW4l1};K`g z7P!9p$Fwf9?$`Jec_{3&T68w1AEN}EGURWm8?1?P=-iz^sgx4i7QJSBeDnx}_^c^Id2-PIDv5@j@txy|!ngw_B| z=Z+Xiz4D#?%gm*Lc7P4Vp=U2s6mX==goo$Rh)3^~!+jm@w|v)4x$PfzxmJ+%{jM*6 z+q38moEIBv4a?0zZm5qW^YTXj#%KYtF>F3?UzIY&(-hG}6n{Y}w-`(VZA;%{UVN4a zIeL}^)`<1o#@<#M@$u^C*g{UJ!>{nC`X`z3+vxt<93E7nmv{tq(8$VQd|0;#29ASY zl3MFw5&|ErEl6wGWcadjY0gCqh%IT8aZeVqf5@E4YeZkr&r<|hldeDa7%7uY&J3HO zL6)bM@9x{SIaxV8Z%xmo>#uno!|OJxcYsPSCPxRDQ}hF*tPJHbwZv`bxW88{!jFfK z=B>n1-l{o?uoY@FW&nfOF4)AmNIER)c9`{ZO;9|~;?|5@0>x4vU21mwK+V$Pk=3MC zo62>TlZkGDJn`>q$Rca?YlVCx=~8SCU{R}4eDBvE>rJ;^=~T6Y>;{#G!Pgxp_|tlq zut>BFJ+~2UnqTE53V4CXBE8BOE(lIU)HXKTdx4)`)#5Jl@VObklIT*exzz0W1}2~i zB+jYFcf!X9-$0upTqo)DaOx!)aNHgg{7X)ZH>(6ogSPgNmj!TM=4?z~o>O&CoP3Fo zDK>C=USu~H`>R_>(wt6Q?nlE^z`CrL@^#YH+CkU7XO}iVz0lwruD2zBbNgH=shJWs z`|pV0pI-PC*~Ih2r_ZGloRQvj9c74|lvoAc{IJ*rlP;CFq6ecPLeK8>Z_|{RZdKQ6 zn+x!W;}ZLjoWrs<>yz_m-~Pr`T2uZcKaU-)){Yo0y>qi`<+|tG;3?rRk=2qrj>Y*a zW(qHpmG-!m(b&IeUKi3E?yDuC*|2B1UpN{e`!4d4MsBs6CcX0M>P6w(+gbSXuP4I#)1n=p54mZX#gky2auCPUKI}3{y9x0+LSD-a zTso!go5&SDzzI=>Pr75;0()r}V;sdIQaVz44Hx2Lu{rNqVj$07+~ZV=YW2E;ET^7$ zcDeuUGJJ8aD`x8`cCY1?%DL?<)f2US{g&~7&5p8at*z|bK*8BMH{5McQTO*9@BP(J zD8ttKLqoE!^QciCuhj5yQ(6o}^HBBhhC8(t;-f-#b(h}rx|A(R*;>2Hd&++9lr#Tp z&p42l*=9Cm^ZAPANgChAni(Gh7Qrel+#8=*R0B5=)rnt-=s-3D4=>Qea*vyrAz9EO zkFBg^d^#(=J(s!_7oF$sb$aoAWN2g&iWthljVEwMzL(Cs@xal_#pCIG)9tGnX>){4 z7Cz;z`Hd1j_bRF33JUm2IoIXG{;7L#FlxLF&B37&_%pmv8P!ID8c^jmMv}lmf@4J! zd;~?RtXacS)?3QX(7F%%**%Wxh7Ivo%9x@1v42q1t&f0A75_4*#edTs#llu5Qb0dL zcn_S2bS^s}KLGBwfWwz&wJ&jmFz}O?w`)zq*ZY=SpXTMJ-3Sx&pRF{ii*L=M4n(wW z4T_}E!-9w%5u&gMi7J5sBASOp!5f4Q2_4Lx2a5p|JkY;Co`D2;zy`<=!rfu)8e;kmLzjTls)_mvT?`*44Rl7Uw+Eg4O|O97C4K#&7}Bx)a|mIc{TjUp8=Eiv z!=sME_;K3x@n?5}EbMJb|D~n%3Y|Xmv@`p3WMNkvTJn8r>0K2NuBZS!=(p&8I_JmK8)l=tIyOYA2%zJ@Z)t)rN`pkkKhGR|7i3UQfW@UfybW~jR=YxD2&*e5k5Ca z?7ZC9ssWW}cH4S@fX}uuP?E^G52ih1PpfU#HST8PcWO_hV#g|_65<|Lw@z?=JV5a@pt=N=^7#n+~fs_4i}o>?|2;|%wRUW>XM?U!oH?2 z{`+2byGWeXD1o9~1HBMEn9r=Xi@Zsnuj=)#th}%F_6w(zC#Ap3Cl5zuC}PF>C$xHU zOc2xeq|D|&>=?ekk}7-M@$OVab>Q)!gK?j*+3O9Y2Kk#nYAIlYy(0qA8*48oqxfPf z0f>Ov*#N?V@E5kbwaL2-t>B4H{FXWrzcJ$K!;*S}^`t|lMWf{sE*;YB_FhD%VTQSv zMSjVgFWu93_BlJ1SY2s&{1=g1p&OpX4apOyd)$^N&$W%B5A}PavlwZ=Oo-9oQ6Gu# zLdndeGc2=7xC@Z+-BprFJ@-uC}oRzk$z=Z8i5 zc(iL{VJPQYL9q5u+rGU8`-Y{+kWM%NXf=?3{13qMk8fB4^w4vB!x9i+MHmoogtxA1 z0uG3Brj>~f|D^NdVXw7s)FQiQ`<;M~MqZSrUQk%(89Dl{<*=Pq9B z(SpPU-f_fqP8vU9W@Nenc`J+J_YEQ^DSGXpxPVK_Z7bxX>EoEQE@7mKrgb8l}+%$MwV}&LzI>ODxX?X~SQq|eOx5I?scx@Xhnj|;_g7Zi;m3`l&O0#=hlMLCyrhsT>EqHcv%!5?KmN>C zBa2&V*7Xb9y{lNckVM71!!errOU}$Y({I4$kt4sgt z(!aX&uP*(oOaJQ9zq<6VF8!-Z|LW4ey7aFu{i{p=>e9cu^sg@ct4sf1x`ZmPY8CrE zzr-j80HFRiUHbnw50|V%nn*j1iOiJMlrf|}lt~vI-^`d5!5CDAl`P(jo;ZfCAh8rl z{2ho}Eu=7su$I8TSKlqW#-WqT%H4L-*}l4p+jg03t#*oa+Rpn=B+mQtE&HF*rHZQ7 z(ituy06>_IP8yfP>v)&CXLyw>&3a+)925(^+803B;puPylsHud1|Z_5MsIhifXI<} zy31$&nW|13{#S}ZsZ$)#b1CldYX>)M`FPq{4ogb}Jg>QsTO7(CH#!bXYN))F20GF% zLRs1_t-eOv#IjBM(N~ky&e{3efyxUmp-6E083L6-I7~O1=k?mhZbxLgxjrK;=8iqj zFw~Jk2|DYA22^-T(|q*|G|Cu@rqUBhQ%z}|8^8*Sq-YA*1r8Aw>E!b0njK055@OEXNd`p|BedbH zEy>;4La+`2c(LJnzF&+9 zg9YlZ#g##Gt=Yphv!T%tjC-+TK(t1ann9zdlZ}g{=QP*=k7ll#; z*fFxnFN^Rjz(CmcDpq;^nlV+Uf+9vYY@RV>)va9eEbJ`07vm}bU{R--U6GS*#9lRF zXE_%`ui1)o`%1qJyt3Kp=UdAeW&jE3oXAi0fK&h5wlnG7+xP-=Hk|5pjx4**?i8&} zX!_$5!Sxw!8bB?sOKB0-p zp3W?%g#emFC-?IGKLUyg<)uM^Kn?(q{wt(Zk2x`?T)RS{mqM+zutM>-pgNimP;5uPDlvrH46xMkzG ztH-j#P>eM0O#!3W9Oz`AR%1{y>Hv8-Spb2U?4nn-jtdx#*E0KQ?p zF6#;;GmJktErUch40Yw~i{24M%TdH`6R82FFD_hKSaBk=abK*9ed&Z?S%$zfd5>rb zDX4d;mqxehV7dGzP{^txrp9g#F`8sS58^0tf1$d)nJ(=Qi)H4FcTSTw@j#=Xyc_2@ zX0Y0idiy%pIv-O9Fw&CB&T=7nx%j7WpG9!{4YUdz>OX29Dh*3IC`f+|3knvE{shg{imKK(4{{Z^K;Q{a1N-h_pWf1+KXN(r-Q zQ#aBl7E&j5r25f`gf=}|20oZ%3=bQh+S~IEi1*1CsuORHigb#9_$!|a6)|FDi#avfVj64MZqTsovdT1}>L&h9NH zTyCZ0Tedv2QFitgpCam_#df};FB@T@nZUHaa{0`oMClY0!)M7wka>|ol%;Fg z*LIe}nHukDre2X|4KXW#KPc{s7g=jAu#tBw>RD{FsDwY}+Cl%8On`|3dOPu1@wLB4o$ofc=YB;{Vvk8VmG`s<&jbf_7 zr`w9*-LXCREk1Wc**ra#LLZaZch?ufZk(>fQk&AtU%#D5Ax)LR42}%9{rd{H^I%(R4 zRegl@)yBR<-}CAsn2%dJTRL~$+{{-~@a}Hes?N-9{pUz>0!N>T-DGa{m}$C4T|wfk*Yk3BE@CW3y4{k2Ze+18KpW3pXdfOo83st8Y+g- zw=*XR1F*ZNjySY_)TwL!Yg(R+_C7RXWn=FbG~4!5C6`ZN=+O-CKa8hDx7MQnPB;}YkN zg#xp3opXUz$k+b%2%Qo;i&`~HsIkJ39#w7m)>1Ph)Wri*+jr|w~aq@a0_-fY6B}Q1c z%4SJ0PCZY*-_KWnE!EDN7TQ1WJW*->>#7e{q<#@GSekvA79!HGmoTbWnUfMO#XaMcBXA+lXryM za85E0#TS)uMjj@vChaA#VliuV(tI<17w4CBb?oklVywmQ7~)%lRYRr64LS95rPj(5OHZ+ zU_qtAKD7<8a2ufkWFGQ#z}^zEK+PP2{uDqGpg8)$GuEiX{-TDv0@j&ne+VM05I&Ei zcHcW!U_pZTLVxf+^omB|5n^Oj5z=m^eU~?#{)Ds-W)nw%5;-PpWG7a%FTH)GL^8mpZ*HV)PDo z`}mg4EsJho%>xqZ-)GQ`xVrM*5G|^ucrwhgIsI-{OfZ!v5Xc$-e*ESQMLPdMYWI6i zQB}Ban=jg@OPAnIh_pvsmlRuUclbt-)&n}4L61->Z)kh7yTg+_MOs(0edT*-DMIEW z#JcV%3HDAU?2r^IrMXjX#qL{FFX9-*@nW-VXEBLVEW1_m=RD%p8NV>C!5zw2;OQ_GxZi}^;Il)ZJ?V@0_G@u})T=|E{*KRwc+{Re z!Mh1zpdT`=C10!=YHlsZqCO-6OjvlR&+@Ak*259b^OfRt()%)OD#YkpUgUXJsb1Fe z?5H&QR+_Q!U*lIlyl^QCUrDxerRFb$%RmC(FF#j=&rQPDFF%AXPZXZ(EmUMAkT>G@ zx$7(2^^&El;e|AZ*E>vM6evoj&9qQ6!%`NW5Ktp`rJ35Am~G+D4_ZNk6J2M$DP!?I zd&O?$yF_X!TGz7@y_Fxn@P|W$Qc9Kdz-1LMv%$n&O)un^IC+C5tGhh?qj;Z2Xbvp= zO&^$7_3CB1Z+wp@7JZyBuyrjTv>(lHVr6IiQaWD(uTA5lSz`m{zNzms?=hA)32#(h zvDtrw=5~ueR_=}NRjIS~4-5qlCYpVtj`FMCH13Rs69t#`pme^+kf=-Sw*K;U687LGI5B{I_@;Q!JQ1f*qaZ7$0q@fQovHq|g_+7@0M2l+; zAk^mtfQAA0=)q)v`~aK>f%Kg50{kVvZ%nUcn@y^|MA3Y@lK}dJ>0C`z5)Eb#CE$@AKa)N`c zW4T;)ncRA_8fTjtZE4(>?3bu?jY`&mhV<&)wjx>rcX^KR++)sFTx@t1j`w`4)?Ual zy`abTkn)cU)sYdJm0vn_?WYcJ_0TbeoivyD4q91@>}xk#Rb{N7o?$C9nknadGon#4 zF!Wq%W(VyPlXBt&^M>Qaq9Qh}TAT_hUk2$x?9TAu+sXQiAFT2e|RK} zG`J}+v(upG#%%az^+#GzeRZt|l!QfD(4IsSESjygNf(V(@iYT||UCfHl<=PEZ2?W&XIGHeZ`xsj&+K;9Xqs)>rW>3lvV13S6JWRluV)S zBN}Ix3Nfhdj5Uhb@y~gM%n0zi2DC=*Co1`lc}``7dc{1~o&e3QT}gHx8oMODwbPC# zn-7Ttt-yc)h;@xW9Q5-FV7U1i-j22F1;Tug_0?Nmf&6F6Lvmom`5M3hpcm%q=ZBM* z&g7fZIa2@)!#cuHhVS|$QV@SXv2qf$BaRMDzc#P26>94JUuc^Gi3I!l-lW3BvKlf1 zKdqIL1x4F``5Z_pDdA^$ohWQDG7^cAT`Yzm!0;X5$a7$zxbmG|2R(HP9PzG={nlfd z+H?=Z8q1NUvp%fY!Qitgu&6;J&NiX#zV%Sok+Hb~Ec{TGj{pk4!AL}XH$`jp&(P=X zgxi7cS_hl$gBzc( zzL@V{f(WoZk*)(&ql2Ky7z%;`x_qFq$=W&Hr|Y1kA}{a`;%J4$p1RN8wCf5$0Zr_U+h=< z%}2ifCcj88MB^r^xxAhVf(rg@?LEW|B?8)IZxnV_3enSaRA6|(Lc+C@TOtwNkR zG{6eFz7<8MU!rf8qoWTpPKl@C26YB)L0DC*f*96oZmagTI?!Xag2WCN$AEDeWV=~W z`B9foX&n=r=+2^QlDbU4qdBNGso9B#8n)P=7>{m+ws++g&jbA|Pq8R~a~Xp0 z3tkz)9DW3rQo04|H!`db{RGEj83)fwr-Y}xH6rJTlvKnN+hWyo)xC~r z;xL-WgfumxC(}X8nN5e1g%4yWVq(9}?CtRe#CPQr&5^&Qs7l->&v*l*S1C)DDlbtX z=pQLE`IaKi|431TE63&Zw-h;lOVRU(^S2b89wLchiCHcUjVG)wjDJf}i2m}bx$b{S z5!ll5lbu4tTbP%V67Vf+bI$b6={ypQ zJ7;qD8Jzu%Xqbwl(&?~#8+44-YbDf9P!``(WOp~hmt(*7KctBJTZ$eO|06{+XreAz zOLkgzvN1NAxm^E95v|e>r=}RbR~bH*^}RxA(K4vBShquU!?g=MB8#Q_iA(?60=N1% zgN7E38n+C)i4Yxkn2;2A?vSjdEIA6|rs*VXn% zY*Jtz+b?MXwgN9@ZMm3DYrifRP7E*?i!~}=%1r%c%RLvdHOr00NSw-1n+6!Hzjtjl zF7|qw1A}K0E4`iTzVwhE;^bX)22|N8DnToii!!+BV$I_h#fZ4(*!;{@k#SnQ_fq#k zc-!!;9sGHGPSqkNXIqQ6M4#+wB#tB+Jji*(+p6iEkir{7kk;2R_?&w&e1CT0Y~|d3 zX+2X$*`=m!rzRt}jTC!qedE1PFZpjuv@fl7t4=vwT;i2GDyFc?N&oIhSPPlda@&6lzH$rk7K#Wjv&yh!8hJOZYsR&=pVq*#~~3> z^!8qVkm$&X{#kGB(e|?4*%tXVrcYXga3tj{NhXf(UOsogPl+PoXHQXfw*OF>or$c- zT%5x6UWk);*u3lP+7}g!wncn_w=Ck>tWDr^x73J|I#W8Qfvp-$$LS|_Ws1dl=c}|Q z3bFyVfqIE2>s9%%3iqCbb(PAPkzfC3gdg9nEk$P9@N)aodDpStL)t^y*Y2guevOcy z%gg=n-mB)qqgnSGi#Dn@8oVQEB6E}9Kh>-M!J_qMg8^@mO}jrwio+U+NH$0%MrM|8 zs95AcApP^eyz{rj$FZGaL5^N8_#e$`vD6UjHrYH0-x;^`_pO*6*YRqg`MJk(?&~0r zBfCPgc+YB}EOLh@3%q^^fOljiu6FSnS-@@3c3v)D_NeSAMO$LMDVKu;{=-FPiOebH5XztV>&} z$|hxUv#aGXG-0Fj+_~>dC`lPgiS`K#zT?&SV3_HtM2qX4R92Ozbo*@Uq0|{!j=#-H zs$xjlzI(@&Zuaoa0Ht_5bys&fc&ESc>2mS;=7w=Q+=3{e%s~Ng2uspLt6QD>V%9(%o+&k{!F@;G^f^wR^Kf!l(QjRIov6L%p;~)|U zZ#X6L3V@{aD9#keJt4zAzB{et55bxq!TT)6ICA1P-0DzqU17P_45b zlQ>K{7*QXRcsx9GtfCel+1*>5)`nKHfiE zw4(N^@Al0_q5s82P~Ti+0Er3>7G%}K!a{hCj@S*bBi|y}y(oDe-H(ccT8%rFGWucR zY&X;QPj^!P0z?p)KveVe^ugdW^fwpba=W4$e7b|f0)_PZ64lqecx9w5Cra}FKG8kg zrunc3f@PWrI2YUAlOoN!B=cfMORFbF>VEz4`0VK$jSvAz;+5FBg3RBHVK}O#@BIR- z@ygbv`gMDf!2)e!aUnR#U}EgI#$XJ?nrEDD$HnU!KdY|Clq>NxID3+tE~oK$@fPv% z^2;!YVy*+DTIE5XzgcfA(z5|$R=5LfVU(dR)X4c_N+6k8@zk@A4!RRu?Eu zaXjeYpEG1a@xrKBfr92qM4%i2!Gz{0NSdT$V*e>m%9@GrLa#q=V2!Ye`ANhYt|`tF zTq#2?7>|7p0wm`z)aZ-|T1by0_S?Hjm-O41rqYITL$7nckn4gchpD%<7lMSKU(2B2~UNyJ96jG~D&-ky7wZzFY|Di(jZ#YUO4>`H#W)-?#qL zokafKon#@{IHGyZH&YN#_}!hfGOZ9Ab z+U&>n^f7L$#l!#g$8Ei2?y97ZVg77dsRTbF{P&Dc=e0EKB#azcKjxM)@5-M(5xQ=A zq8arzip{i-@9v}(64SC>GL_WtOPU;iwBbAMM2tY{Z*?0`NiCd0AQ9STjRo6;={{oP zzYjgJ-?)Tc$I74bv9e`Cq{RE=T{%DN-@uM(`1e_(t>yI_m$tugN%Xa9Y&2_Vz}!3a zUA{xSsaEk7l~?SsFt_8{oFDyT#gln5@BGA2(5RdA1N{=H?Bg4k2LA^xNtTF6UCDiC z+>Szc-@Y?$4@HJ$zdon!a3LZ30_+L@r@h<~ghKkhvFp^cOAt%|Q)WYak3Tfp z*8=(XxBw85!2y1g4b%OAS_pVRjVlm<3kmS+n8J076~y0!E0tW7<13bb4URVZ+~h@f zxLLlipLC4cq@4iYuP#cgM|^Z-x41r{uF4;2X>2x_lGrdCiGbQyN3%D4+r1(EUc!d7 z{icArNINn*xAeXBGCvpJ5bJy*Uv6}U)S2tT=OjlA?BJm28be{l_<#wh{nuSFgl%&@ z)0DLA!zkMWk&Rc1p{GdD+q3PHUC+H{lZLC%6aNRD3{X?mngoZ>^y+8&NK0Gqat`-m zUeq~Z>XVH)h}R}8o(EJ`BMo^eZWBX=Ji}V&`xMb1Rt=vbyza$J&Al3zaQG^PDL!bM z#+e+q*)}GUWfNOp>C^Dv-PW4U_rGbF+iq>N*Q+K`PE2;0uj`_3e|wtB6QBLA?DlpT z-*Zm=-XYSfF{(egacBe=8V!SFCL+cYTr4tYUxSuzIzALKFKr4MJlV_w{Th?l z9T?Od*kj|c=yL{YD^+@_gqEIE6E`jniNly3@Qq8kXp{fo(&{%Z)o;*$|9ldI;jT>> zJ&jeno7+KrHzY9z-Cu^hIxW&Mg}H`HOY-KgvffuO;B&6g@vJ(ZDr7>7Xy2EAyizp? zzY2S*TYdL_Ei+Zlo;|tc6}}PRcJ=6t+>calUGbb=g!m`ueaZnO0Zm^^rZ&tByUbf< zmHb_AGCFsk?sfFO==j+z+{j+VMBK>54>!t~ou{=B%HK_)Q;sN~dHr8-fLb8?%o-SQ ziU0SU+lEV!Qb|C{8P@kg0LgD+g8pX`$=2A!o!;E}fB7bW8G`cvu?gTpecNjN2cj>* zxu5)E%y0R1jXcpaP|@XH{g8UtM|I;9T$%(rImJZNg0Hvzye6Dw8X_FjLI^F+%}uvg z)V!;60!E2RE-=&#_2})cn=@|2)J>xwptIqWaHhc+WI2`(cC?Mjd-K58;$3N8*|^6GO|i5mD7eRhV{Q zeN1$1fY+Xhl}Gxj@02{@>MQJxd;;doZ%Dtvj0pOfu)o(ICPWebawPM}V2X_L80P6) zX^H8jRC1Zft)OVot!+P%%wQ9>-{W-xLl~I^vdEKluA}f+1PPwZi+DU6sRF{%D9NI4 zj5B4li1?LpM6*h6(SQ9NlJ%)+!N|I1RpDV3r{U^jzJS6vw|le#vb@M{7kc91ug@@9 zY#URN>TmY&1$}{<7)fkIt+=s8q(`=Fkp?eW)L>C#pp0Z>(qv_Vy~i;cOTG5juY-rPrXb)3`#s+B*A9eD6eZmdS4_jgKtc9UzIX2rGW9( zI3~%9GELT+`W_vtcD-IwP-XfZz^@yBvQ9>oouOMFWukbsB36!~YFO z5^I{#`+sgXx%J^xgma;1fujHby0ZVv%D*4be~u)iBw_1p;%I7MWI}K0^uKt;$^UiH zzy+_3*ZN3IUXmVTiEnsE$^nPPljjY!+Mp`3j?H-7f$^U|X~PI~F{Es$Wh7MVr)*yz z*Gx70^7ZT|o$Ye$$ecmF08ChaAW?|zkIk>#zAxb~<9k!WAwPUQxTzsScVC{i4oscL z8gPp1TMjLoTlX%?!KkA!1`)r^O>xF8)M9OP!*tnt@00@tsuD;_i2<@^x`AR^V_%vQ z9qhl}&a6E69mwTxl857%*f#Y|l+1PX#$vP$7Ch_D!pX9OEc9bMT^ebM$x$tc4IBx? zvLY-2e}5}*CG67EV_Yi&UnA$E5oi5XWBrLNXRIlXH0eZ@#za?k=7?6D#gP-$at_Uy zJ^LJWtQx5;YYe->)jxS_O+^gQGd{xwp8$BmUlk<}AMe;x1&-uE4J4IDH#CyeZ%hH> z6M8y8?Y+NoO_>OkR^M`r8~C^L&`SYCl)qbvGQo*Xzn=wsE+6kRJO?lS@1NrupbeWC zfBcU5uJ|xc50q|lFw?LK`9|joqZReHX~!*y-5j_kFNIq*de00yfdc>@i0n9?UOb-` zuoGDaNbyFzRLh*wFn_0(PJ+#Y=zK{r)c}Q^PxomhdZ?0?w9KeLPD3ad3e-vTfp-PY zH4LvuK}xeIbe3~mo|FR(*3>8(r~>(BMM=FU`aGg7ZGVe?ij7L(lC5??d4Ao$8n5u< zbt;h@KYGUZ+9XF)cyv&QOgLehLtzR#MeLlnp9$pVm0K!p`@SwL6*gk~Nlam4@=~65k#9^1YA@4oEYaN<$_4M+mJ5?RadiTdEizRjX8lgyjN*EZMOM+cv0ns;5R_OB#!l}eOih=8Ftb_F2VMpQ6WOCw$33Y9`@w@3BH zu1x4FgwYP!JwLHcFj%_YU5M4`WNhZzb~!6pM=6sAq?M*`ShfB47@P#!lDD<{WQT z;}sd8wUev46R>BgO~(&-ST&dA5+&U&tp<9+-*liWol1XkO{c)0P`$oZAI6m@eI9rF z)U;<#Z&_c;*geepS$UmJPrZ>(E?DjAZ+>qqvT@(LhogJZ6$jU5AJ*#R)#K=JT`XR$ zkdo~==fg*^?TzlE@Cle@wkP_%t5>)#K3ri-rLJHZ6w+RRE!dE;g^rfg;v9{pT)$MG zxDlY~j+7)}+9tmc+$Ds;+7`**w3QRPbuXuN4A=CEfl<=G_qk~Ayya*RfQ}`Lrw+GN zShBwQnLDn0cy4Sitxt8X|3(;-i8u5jd-0XvV-4V}MkP-hkGb%H5YWt*jJ? zA3rB2W~yczUJe44#thRAN!T1RSyK3~hJkgB1)IkFDBRUT6VlYDw+|E7r#%}-cOg=0 z7Rq9O^z|^6=PgR|TlKTNy?Z~90jd&QOG67%GxH*0z>-I7`k1JDP+m3okrCJ=3rw$| zRB+)>*AX-Gm66dlVZS=?*AH`V7oA>=fO{*Bc1w1tdR%seD{Y9}i+M}dU2Wdd*-dSW zcJHUqw|dQDvXXU|8#2P=X}f#l?F0QKq9*LOj`s6B*np}Phic8Uts~4}%X1GtE&}}; z7AX^-Ie!>dmqS?3%|f{w+7&2nU^zIYAFDmg2MsZl8g#@5<#UpQNwULEDnn$$GQLAj z%=!mW+h1L8?@DU0O7KPL;SDFlW@IN-6DsBr2%6A>A^L`>*a51|94u`r6JpdDZ4GEp zB@8mLueT+`52Bf+&p^JO+GgY=u7`3L$e#WnAQfiyt6r3~A(m5|=O`rk!Thy6tSI30%Mmk<&L z&^lsHLapEycAI5;b5;;}X^9VA-nvNGa(`}s&|Kj?+U~rO_o!B33b0n;q7}0est>P1 z8%=3+MFS^~nl=cgTBe>xWoAGW8u;L;rc#?*>msR{lBpETGLcjdo$??(E%zuoLY+=A zNlX3_$)cEJJa$i3X*e%CX{(MM>N4}REpT^HOMlZ+W}Y0^R9|p5f$?YsCxGD%Bb7Bt z_KFQea)@n5yeU^SRfMwMgT}B7@PEWskG3l9r}inFGR*m8YZuc_b7ca*4PP%qY>1Jpw86XcmLJ zwLdD-Y{r$jTj3FI0j*Vt#cDwNjoFko92W8+J9N?f!&IROcp3Zp>%KQwMc16Ky(Y8< zA!t@(%<=<2kMrtIfM8Q-RRN4^Z-*|Z@T`tewIn@jv&uF;>715z(MLL z7xuL+#Qm&UXME5Wr;*l$0a6ry;XV6HaN3R1#ONu2AW{=l$&f!x&jYtGyn`K#p9~wv z3-e(MaqS+O=#3+16KF{H%pjT$?}UMoy{`cSP=Pp$EQhbHpz3(f0k_d}<<5n4?K?jO z9o?M4Y(>26u@h{3aF#Q1c%ZJhDBVg_m7C6rAlQYWjn&2UD$%p2x({StXI)F*Lyoy} zjV^Cv?{w_XChENav%sGjyZYXqeDZx8K2dzJlw{_{i4V7}XZ7lsYkuGrxQd2DSlVwa z6bL{)$}W>v0Gf`}gOiPRZOTP=9L_s4Hm4^>2 z+g|kTVRh$SsEDKd{4LNW36NXeKA9gnTy%FMWH5;0Ct*bV-u}6`)8@z6f}h=ulZ)UQ z)EE82i6<<;Fr#)i=85Ik(t@d@tJB@h#ofuKa0jsyj#rsU!y_EF_n9@{`>;O$`TQZ| zFS`dUbPahT&&^|9*CA*j0NrshJp)0-?i~Uv$Smc^D zDp3{^vNVGSY|mZDv@)qC3k(o9(5#zhacb=YAf6}T9g{$kIaFFx~Q+Pq0k(> z%oU@9T)Apv0H~KL7pRu)u>H*p(i|1Y_sm!2kx!Bja!VpA-wvC5>NZ*OjrnDkpB1VP z7BgO|-LC1FYAw%Nc!|uIxgKI`K=t+pFhT2>KL!T@6~+c3?hV_r@QjqgJrAUii^k7y znB(ReqGMhRbedwpWvHx;Pny;ul8@p7r1A41_3_-UH+Fis-V1YK6YOI#faVeM=Kb_+ zGUvKyA^Z@Ot0@5rt7q?l1-e)nk)S?QV9qLI#yVA?7?Ev;my3ooTg8_93cFlGcalz%~SlNC(QUkkuuQ+;2-;#6DkkO_zE1qCKy&oGbS8J!cNXU|p{fH;= z#%&XJ9}5<3PiJC4+@P_3>F4s0`69nL`!ptwszOgUTeLc&lJwc8(kn6l^<@trWIdjT zY&m6r*wP#G(AKAfm1=RMg$2IO4o@~MF2kaezMwI2F5e)BIq^3`Y2?=?54m%l@H#ri z7x4LcGW7o2yu4fws{vF&wr-ad1*V6lmxr3G=I$1~ zjSDI>Bpfvih|kpLD(x&8-89=vzDPsDQqA{r=yQa!(j5@Cr)QDC-<) zuoMWt)N8xM&CWU0l&4w8lo?8Wm>+P4<+#^#1zfw}#->#f=+g$%LJ36%lj23F+2J$; zi9BycqE#~TPqhaOaxb_zlkh{9^iq}Yy^bOEy)H^*qbcDe@0+5nl$Bn$412R4821`P z>$p7^x_V;i^-jDA6=zn`Kg-*Eyu5U#%NSQWlZsXMaLRj|$A=Ms_i#kxHEG20V4zI6 z)8={F`gb{<7abTzn5WKvGg-FJwajF7{H{(ceSP-% zddI^o3W}qSGESsx7Y?}m)dB%Gsu9Er-ulAv3wgsGnkPtAIt#% z7Z-VinsP|-Oh>W_{`mTZuZfa)IObj%8_tltr0kVd-z+<3*9y8iq*5^-7Gbn5oz3)E zCCas$Lga;fh%4lebDuVA?osG^l@8Fy!%^wl&a5NNEOA7cZY{5ALVwcc%4iOh=y4_s z2qrVl5mFuEB3V<;SoH`DLcmz+l81?}Vj4`pTsTV3*ry=iCq;qtYNZ$&CO(#v1Rl@SMuo7o-(X)%tXIyg!P@K{>0y#gI_n?=m?_GXKX zV(9YO%FW&SqQWK$-KT*&Udz`gu0>^zUH(mNU=Mvc8w1DvilCq%Rk21*=tqp#8fCJ| z0)wf-(W0=@xcu~{!b%UZ(%MtMs?+W4=+nT@*~Pw=sDj1dxqTKctQ?~7p<76~9n8Y! zDJvll@aCLq8d*vJ#1MKH|PYr*Igfc7N^6XK~ph zRQp8%qk9C~Dy)$Io!hG33I9XhfUg_n!DEFJ?b(T+w)lBr*XPwDyGZrToI@!&ro9I)X$nAZ?9E3-d~HGE7(0pln(VH&Wdcx90-*L&2BR8p5N z<*S?(d27h=;d#){hHa(Y(kY~4vioJohGAU|Bcc2k2=f<@U`1Qors@QgF652%eDhWG zt)1>MzAtoHq;i8?d9_!qZ`n8+d3BizJC?XJ6`1?e&If8L+Bwm>RlD z&=>rbqG9`nvWuh54X7$~D-sQ^LQb4SnB1Dk$YSzOXjJM@I+<0O$wdKYy(HqOc0a5? zE%oy%VyKNJD}C@OMILt;rj1+g%Dw{Eu<}91KayJ!i*f9xxfLLSe$sA;HcQa9Jdwhd zcml`HFzG!kaD`8+Dm6pXK;{$Mjz?E%g-QA57chcc(4^nn)7SMnV4%SV3QYOXEb)fA zaVlJ8)iD5U*&*Z_%r)at^5k2hA}O9_SsEfGZbbbT=rnRtc;&Wtk;z)4e`nca3pS0& zJe$n5$Da50j*dT^0(aZno_VNpdC{Gd&%;|F|0tZCkv%`6R=TUu-_{&4Ufe}S`oX4o zCC8x2wej~g##-Lt9?2(tSzY)4;O!jOb5VjU9NSL5Xvel~+ctJ=+uE^h+qP}nwmoNl z%q`3{bU)Qqz1~_2`hZhQQX6u;h5iU6Su?{I+z*o9)J#dv&Wf2-3d3<|6c26){F zy`Zj+)Ap8!G1%Vi0&f`angrNP%2twO!9y zk|*&42D6yTo*Hzwr_2s`MhDhSR$LCnNp_*_V>;bV^|!4@qGd??yF|mJx?BvvjjrlH zzUy7_Rm(Ih!E8!p-k{~Gcg66VrTuq+NvTaN@6%i71bA}r+Fj+g)RQ$m3M13ilmZCD z{{)b~L(!9Nq0cH|K{eS)^V?N>djrhvKvus(k;Hmvjp|ZdL#X=4dcU9ZwXF^>-XV~;Ch;HQq`w)skv>Rx-{vRwPVb#r2JEyUp^rvs^2UTHg|W;nti zpLExw;C6N6Y(tfDJhtfC4W7=(!p_7XCt*d}Qiq!>dXMI)vj> zF1xK&z`UUf0MuZOG}x1tl|jHzMB7sStZjKHX-~+!lqsKD_Pyws)`DcCSH%>Oa&~#6 z$yj(e1{i1fUo}M07}27X+`1jOFkuExqE$Q9xg36LA|KcO`B$HD!P~3W@CQVbzI54p zv&7>ybGsKUR=3OD#^&o4mkdCrqC!HA=`K@^$d)*@;u3JP-Uauq*2%Nab=Cq11al8E ziKx_zQo?ji^kzN}&0> zauC3s7ZpXdN%A?pbuI09;mVdIICa=G(8$|b&R!-4w|~A@DU>i^Gy7UmZMBV;;Qg=7 zdXeDU9LRPY4=3Ft?1w+L8h>fT7B}}N4c!68jH^62N05@^Ep<*9HPLs)C-jZbHBmI_ zb+RRPgV>?aA0Pn*3oisN+qW?QN#je6@EZ8G!TD1~=;Qf>)(v?wnd&*Vb(+^o`ja+F znq3tjK^8FsQwPl7#Ur=fpoVC&fFL^mX~S6b?8xfeSGl;E;izOk=#T^SZ={ga2i}0f zw!HN+p3X%W;7`M=krv$K;f$5-5GD}Y0adpre_+Fbz`mtG)q474XFgzvB< zUaa+=2P9dX_j7HLviP-{p_xE6>Gy9{JUM1#vmAe^25D6tE2g2n&#}Vsl}w*E5Va0~ zHR-&saM!wzmck-qG^o|NAN=OH=l95@qty+b@uQ(I+?|a!P5SozCFaj@Z;&Mb`RZs za=2n#=N-ov6_0Pn8=FSg8x_rnyP{u=h{rqa-1`r8`XeLV8GeSH|JbU-DM2qhivDO8 zzc+wH&le9xVBF4sWPcTU#L#D&Pr~k>@7Ir;rNL6LA+Bs9Fy z+XFbL_rsASsEql<3f)}OM|B{&4FBH^AOrD1S*rCOS%Lr1@nhP$2NVOa&8;&$$$zG|n z+B2OXdNcit+PiIb&P3Dqm7rhA{~mgl?A)i+(+J=2H$gM#QAfC8wYKsLr)1lONFb#+plt? z^Pi)_n#9De59zN}0LmZqK6Xsh@u_n@X%&gZ>@*}bF3=AO_f&n+XEl=?78kO*IA72J zINTg-a|B0{r@7qm%#wj8+-Pk5{c(5cKB56tZx+mT;gl*VeQFI&rj8rYOo#@lRp2paPZo)Dqn9$CwJA{QTQ=O3qr*^N>fj{ z`7g!QMu-2JX&OU#Y$^y}lnQVRTQ;cJ*CXgbtc~CmFN=LdBeo*9K-S1bIAz?11>Ay1J9Vn_a0=0O-RWg`jBjIhTVv=p;tOvG#7S11LcP^125rH`CRWhAx zb*gmrz%@sru3@kvYDNtqV=Kda$3({DJV0y!T8@CE5@-TS_#`Lvwh?T@5leKSE0uml zH%}y*?J!jiD502A%}n&FN+ZO~Vk-K25|&bp1+4za;o>T5cMGI>XbjXYxEKVVf+M;O z1Lz8lUwhAr5%Oqx%Yh6@(K$>q`_w*v87yaYuOORJ%0F~7yZ6b|Xxja|e!}Wf zzglSGnRA@U;V|3PP$z{PUwT`Eerm5vn5PLMoIln_EdN>gQw;#xa+K8{g0lhF)|#C# z*ZW_~hB>N-sM@`Hao;iJjU2;C=jM*dENFQ5=?BBXXBckeV2N^wHe{;-uxXO{oa3!V zi(MSZE=FZjwd9JCfehnEyv1ZCY_f6amLPzvp+KH!xQj9q-4+_e8;>bC%Uln}`Ry)?+Ij>>z)eY=czX z|Fvrm|KWKjgLQws>)jxELH5EHsvH;FA;M8D^oSb1(y3N##uHk0k8RG(ZcMH&aq69G zX$*>M$jqF$5#naE{q3jy^y(--^E*tjp_%&KZTz)*x%x!_+^S>2!)v!RCj6w{taAuw zr+h{|A4?Fl>P6&s&wbMGuv=ov=O?$;x?M#7busE(*uDgXPo@TwRV;?Y!Q$MQ4>p+N zL;4$F6tTvl39lDRpFNteH!LiPWQ|4Q{dhL5aTP|>V#dF7;LAG?+UekA4jDkLEyu`lsR>(K7J(yA%hAC!DYLRpb~J&%`O znL@7)snxlmC+OG;f_IBNu2t~(37%pnTS(2mHy(ZSZp_EKyEQ<))L{B~y%%FVY#wB3 z=|A2;4}_mTtcAxzl&5ky+#mOw&AWdwuskm5SKw&A@8~!9dQ{iZb$@zt_%k*rny_D1)W8E>1McY_W6K~li@nV3z-54g^s&=gS4^vp=iebNY z=7KMx*SpS^QGx2w^fhjgkpq=DJmrHmK|N;XTTbW55xdaKC*FJ+bKH}v+uWz4 z#-1V5Fuex*J>*T1;IOQ-2|6HR>fSkv1WiR-yJGil*csW{k65u-mRU>qMh-dk3MpY# zloB(;G!N$tvmI|0Iksz>F#&YQF%?Qz#8hW)88_nx;XIQVQOfI{p5aj!K9BgU@=@y> zsnK^@zqKkb%j8ibqOcL7Yj<)-@!-b4>_Ain%0z8dM60)xBxs+y+ZPkHV- zPfJ?5wget<1rGP-)4A=~bzQyDqVw0J3k=+t!F55$d8pFgWgjxwwMv;C6G z)fyhP9S@xLlCbu^arJ1LzT30We8tPN3K>Yz!4cZGGb*Mgxf>?=_t~zC22Hknj-m?I z;eJ($I2taxKO?#RQ;q1~!FVlL3)gwhpv*)9Per@`DwI&o$Eq=KhnxB*wD5(*#rH3) z0_z4o?d^!smZ@U#XL7_8A+ye3l8wqtgMPlCk|SmB2PR?cN+b|})-`YBQHP?Q&X_pA zh3(o?xADJ(ZN!?lxoASSQhFwqFBr_|PWIeMA?Fiw=t zc`$qgL0vs42dC1{+E2L#)`XtP*>6;D%YLR(0HQG-(Ibx94j>-n-=o z)K>=l?<9GQJ%wo0!9{>4~QUo zQ|#rjof4QBmi4Rap+mS2KTwuHKjMk?;|omOL$as`%57SUTc%a?A__70+K>tO70RP< zO*5v6=o`()3O=&)V*qBwyWb4T;^lDgRhPY(G_YkiO7CZoui+tqtPP@m*OL$uZ8H!R za%L^WQLpk<4MHFRpH1xFGun4~R`iRjd)DudwCNrQPM{jszhO)YBc?|5l&~M^n`~F3 z9SYfdF)=pu7}yh3KTGr}PJ66AZ|5A^0dn|c<2coxReVTxiBKjuUpRp-36p<4$O|_u zO%)Ab56265YqPXH>!#~zO~xPmM0K}6C-3^zw0jc%o%I3trgTCU0{x0>9BlF2DcDW{ z+VA6<4<3y5j>BS@zaak?W<>jsM1}(TUskUVmi2?!5eSG@3>XOTA9(zKte&NVi>adr zEfXCB9TT06h3)?@uvevRle#gI@ROqt9Eqrg7!7fCm=$f0Xcc=s^cwTuIqH~W9`WM* z6=;d8Tbg3Pj^Fjoy;uPRN_LsQo|wgbG*@)!g!~C6mNb^r58L)E2fjz&Lnng{L19O#G2{yz?4Scvjemi zo7?4|%6l|NwvMYj=2sBMw}{gM>gTz;BlG*2%k0|QV!B&`k6mH@X*R>lUQ({{ma>=Q zqA3;^CtkMU!s7V3iG7OVE2i_Sn#FBWFM27jOxAlhKaXn%)Y~K%pu$905URJj|Dios z3V^GlcDSZ{MdFCAKbgA95fDYF%9Idc)9wCx!V`nmJp3awL*qNX2|99ozyAjUUAP77 zVAi&NF|LTbWriz4Ri@+5%|FX_s~BP#)rRy64u{q(lB<>SQe(nyDR&0A6Ibnqz)aIP zrKsc!r)H6S=Y;KiPUIEkCPA+fX?~BAE51wTs>W`zvZ8+&mg|7LT6a#=Sn0dxS;u8^e_8|0* z(jbbqQ=2{`Z|#L96;rm^Gb_*w`a=BSJGwq8%HRoWxJjghdBs@IJyb>Vn=z=d(GQT? z^O^e->M)6Iw%99jCaL_8br&Hta3Am2m)KD5M~wK+T)+;2E3J9mi}RaD4Va)cuNPiK z23<(G3EHGaj~NpR2^1km&(|iTP+oRg^;y_Gz3@)2YsuDV$2d`qL5^r%%^?(cnu2zs z;_q$pwKO?n2x>o=?=+HM+M9Fz2HwLgs|{^`Ca;~)FAZ&{>6{SEXO<9!yE(~(n^>vK zanYa9KdKR@##bBCH~E=m4>RC878c1lQ+8q!Z@W}}o+ft+HnO8SP;Mxhy#-vJ7;HRj zRHP=eD!Yj(^x8HY&gD~Y6d{{k%4W+tm-y5OMj7Rtc7^aw(c{>K;UY-Z%hX_Ng8Gmd zEsdmHuhWAJFn2O&zfJOO#0h43B$QlsK^~~L#$&>M^brEIfm`)s$C_Lbcu;iLo1c-} zzj?JQ=CQLQ_@=&TaL$==#dA($cxht&y=8c!&x-=A22D6MmO-+nk|#yjjP%7Cy^7Uy zSh8f4DJ4MD6)D`t7daI37W4IUYM#^QDt$NZnMzhbK%4VHJ3vsLwC{C@!-~p`LB%!p z7#T!7Tp3VCOT1zL(4u&awjx;Th42ExIg7=SAAKcbSDKkE3Ilf?Yx_vQ7rY=|MiEj=JO}NoF9S5 z#hWtX5Bme0&cvr1RAdzi1RAujWaI&2f_46prs6*rgO4}vaXqViV2*S6hlPm#jC+$D zS>62hbdE`%=s3&Q9pHf$udyFQxOqs|KV_?xvWtSE^s=Q&JqEa`HaI)ySJC{J##rDv zEC{v7qXM+oX|b*N%qPJ1jT#bJA1g7M_h#Cj9m84feDwNbw$v^>2N+ca5ejoFe^|xH zWtkMke`%kCVNWVH!BrK3x2p9HkdXqPylOTWglN(NOjU6f2+f}Pug#(2J>yr)%A$hO)9PS*O z)mp_l<^s$#9w|tDmaWrC`UsviEz^hCt5cV15Q~DdAk%b=9VA%Acduc5R%fi;&rA*L zTuFeyz>1-uLNqZDS^F?gNoPVg1FdkpLbN53Ua}~cbjEQNaX2p9``)!rEm4CT)gyZ6 zcm-Gp8Rrz7tRQLRRjkzxEQ;cx9f4$spehEt&7KHSZ+AMLMg?pZ-#+mlO%u4k*ns1h zL;_JHNR)c#Jo_Sum;hZM?u?q0MV82XT-!B5QODvrdpuNjnYkS{C^0bFrL%VYZ&9s3 zQX46Htn;HVy9PozBVQfkEqdaszF1RQL&}pEC|Vo+ha;DsRgfKUht&Mu5%r@Sm?(I{ zkxNO;iw?+<1b@Zj%(q&aB~7D9KtY8_K)mg6bCcx~4Wi|MM1xX2u4wj0q2m5{W-Xp8 z^r#fWymMmkPLUSaJ1f`WD@l1xIgK$5dGBL1m{vw|+PzDQZOxKE-*OPKp2&TpnMaxn zTt>Z>j1~5`ZP;-JI#^j$on3-RAV?_PbIL-FnuBaC$DDrxmQH8~=tpYEFtNSnvS z5(goQnaEDz1B$6a1OFyxMnK>{n9A5U!*T;K^L?UC?F@M0A{KwbyUO|foaeweGSz5L zP&Y*Zb1JL=osA2Q^5F>b<$YmHXK3P$su~led2|a3j(-nM@rOm5&%uEzVOk&Tb-*Sj z?zM;Y8ysIrDD(+C!NPB_e}B6pp?k4}xfc@z@c4=5ZN&$lXz)k!Nc#atTrrFo1x$S` zbM2wFwD|^eGIBpFP(Y0iX{--k*%r)v;_>Ui9`=o>1iKD$CB+_Xfm62^1 z%oSJWxH!xVmU{isS* z`AZ-Np@NPqM;(yr)S2G_;-bu77oe6);hB>w5(wqC}konr7zf)TGV*|W5P9{ z8ANXOJ%r@qmd#hi7KM@}Zzd|N6*LQLxY?w2 zs2JF#ia(I5Ou7_Zdst%rxPGl)Wm1oa8C#i}IF6a7hgk}FB#(L8=9ykryXMe>7HNeH zG&OXLZZk3V=V?o0{_z*q@+bU!!HcP`SF{!&S#IW;umVz!mv!c{T)OE@kylClSLmq(dI^Nz@(IQ|w*!Z%0?k+Wp8YEWA;5^_ARiMLpM^x;%<~6zo~;aTK|uvuWE05dw|uO zzuzo)7V@F~)U5X7e;#v)sp4DM4>7Z0ufp_0d*_Rzpg(Y~b6q*@d3{ffZT2ho8Ggn3 z9Ty(zJY54p?T_!^@|+;k4TBZh1gA=sUGR@sm7x;`1j!vx{6q&LmBG`8E`h~0j4RpTbsF1yavsGAqQS6VcF z*zGTGM~gvK-=*v*&m&=|lg6&i`{WB-$itUvID)5{k7P? zG>n4W6d46{i{J;+zE~;IhoW@ ztqxfyEZdPd@2uMXn1hnTYkb%40Upz95{Wv*WB3vG~o^Gewl)TO*>^+K6v{>QAKD_r zJBac*JU{s~vkDk-rpVg{S)|0M5!swZFYgQTGsp*5MV00ZuNtR`Y$1=J8+*F76$F>jFadCbDvu%b=T`B7kNj=V1_H&> zHf_nQRtd05zSACOT52{>hQ>}llFcyY(;eiPe+WtZy*o7zwItu%#t1& z($Vq9v_YI0Pi;8jITA2Ns?ED4atXNlULW`s7l^7Mv8&TK*EDS{Yjr4goz=_?#QkNi ze-tH@u6@??Z|HYUpuMJ1foM}t@dEy!sk90ow#X1&D5dLeS zbxjD7iTIysDs`1rx^_n@E@{V38m*yeiDXpt@&c8zM(cd%uC!VK z2+;spCA(|9pW7?1LEHG0Qxx;6T|26^f1rKp(b3I7hzsSZU%PBzq$5UviPSDURElvS z?V((S_@ChXg?|ll90Q+T*)b0oqEbF$x`D+=tb7MGeaDy@&VKCqZ`$s#B}VF{J2Y#J z{bj#B%&QJ1>M!cM#lyHe)t|a8ZY*U&inliR3JCyIl^IPjSFA=SI=Iev#eYkWYEax! z4AkOsUgx-ZV7?k^bk@~yb;muj#$-(_)td)KlVHR+uzDFDRf3zDtMt{tk<*AhpfAgJ zwa6Ul@O^*puFlK-%pBSzuJM>GXH%FoXjENMrVjG?;}W zAW)R}9LisU<$SeorGoK6U0xTu-AL0R#~45{34@t!wMi(Wc?^ZwU5@;|bmwlO&<7g7gL!UXeIre>ULP|*coVO;mSOV zmV5YG)}iV%_!3<7djEQl$ao0o-C6tlti7MgnQOM6h|v+g7P^5;i}lIgtZmMBkMlq! z%e=H!Q+0yB1Sv~UG1xz?Ap|gU=55EcyT)uwA^Z}wOl!_CcYz)mi8z7H{~dT4DwrlC zPd+8_b$?0Zu%(T*Zntj_gH)0Iqb2rAroyV?_%#we(RXrix5N5Wy%gb!aaGfYS>JGg*E2XSB54Ok$b4y-$Pf;3a&7D^@GVoVSYTZfhJ-a;#IH*Yvd*7Cv!s{r&1Pl z6q=yhX;|`!Fr*SQlQUwKSb0QheIK_KYB40t7pXHQ_+t_sC8>8#)?jTmq)|x{t5YqR zUf!>Hb*5#jC(YlWv<%K`eW!xuPOQwR5d~i;1~qSxpS!jy>Ks-!^9O)e zWX?o8lGsxf$Fe(CNgv?ZxSMYJYkTwExyhE`y#M>E9KKc+%AlQeWb(2N4tXG(Ws;>y ze*Z5!-Nb?nvVahp5u5?&?>KUM?ss)*g&#;>V*9JlY&$18#{6FvobBah)n!$AUHRJz zeMqUw{0!MrB$d_lJVz;AeN_>6LbRG(9CjX3oMOk-D6?S2ljqw}$lHcdyHp#a-X$ru z%#ZA&LAu*CD`&RG3LcxGyOkfFFzZZ$jgMLWx#XRLBgxU=9x1<7u%#^*e8ECSU*N`J z>`jo!CO#f90<0dy=xpveetx>Yz5|SXY7S@XxL#h2v&SYc-)~CCj9Y=0>I%Yc`m_2l z^R?vz5#O@RzA4_0I(zYtgNd`PLFd;V3vKHm0Ykl0O70@I$|x>oo;s5=Z3C(GfzfO) zW2xJj8tA1z>TQ1!{3Ij^n2d=XGB(uKO8mcBr<*~v7LPof(_~wV*XkG`EjV?oE>{z9 zuvu1EwuQ`#r?;mGx=~)yb|sygrJa4Pd~?aoy|nPh&1xm~m$n=^jAEuAabZV(`#$@4 zjj*llmf_DwM^thf%XcPR_{%812gOgx+ykShY@3A_w_yZ zpOH3u=Oj4BrWQF=0$+_zBuKNK7;yL*o1#%as=}jHmF$fBE;Hk!vz5dlxafcZoBb_y z21fPA8bQPvhCv9?4Q}e$I1LM7qD(eweDIeI_$82Ces0g_oRN?HaI5k{8_mDriwFxhN&_yM4`+0@L<6mUtD+6vFUrUU8KuDA$hr02MX221{3BE2vU&!9W({peb) zaPqy1D^EtgpsYZ^p?dwtz}QYvKMmE`PS++)1!PJ=rVe;5{(Ig%{bQcuP6u|i% zI~cTxIOuD+yHV8Tm#(4>;QPJ5@z<#g#bQh4iS)8(oS2&c?tCHY(KOPJ%d>6*lYoBF zq36v+(%Rr&JlM@WXS+k`iJ#IcFsd|<+m~d(GLHcD<+EX^k}n^fCw_cbvC@^h(SMy3N%zMjU9N4`$r+pfH@hNxbbOKg5b6eUVQmz*%F)HN#vrWl z@7t%ir)qw zSv?91tqPca>TXLuTrNcTbf0AYi~XfFKd#!{=K)BW+d=l??P{Ya0##E`tTNSU1>?^= z7nUOH{j9${G{h6OjhrxJE)y`Qwaqac#?G)U`+sGP3Clv*4(*X^bJ6I$YOQa21tYUD zLgn`Ca03<_ko=~^ z?=bMF_l)>AM=nXbX-1~Ke>OL;Mksaw}G=d7yt{yen`R^W#ts?Ujh{?As#P&h6 zRG)QE^E|k={Pkbo1s5phej#bB3y zVM@0{ps#(s66rjbsxEw7SM?MwRH<1{P8stRVRE`7r3;PGb4;eoc+YMvCwEi+$MdUK z@n41b3KKq0&ntdTZdi9U@0IQAmgR0@$+_R+C zTv~%HIGXRboyf&%>9eR*rIkVEWA(_e^PrI8V4`Z@5xFBIx-$!y-C~TyX;PykN@+1L za&dKYS_dCwk+>27{mh$>PI6tXorrEFf#bTn@^Fdg=3{5-+gP`9_fR9%B_1@aUG!st z!iQrqn)87QZ@ix__3AMH8`dVr0Zv3o7%dT43V*Ufw#6sP3idc}h~FjS8P9Nl5t<+D zYM8V2ezfjFqimSTEH8Jjkm=1)g>rPTQ6g^NN$acp;Ldoj`-Y7l_`Ph@v8tSE=qs*f zU?}nt4(I146(0Bc)6?&=yoEz!DdYOq)^zKm0@VB88ZIJj=ouVL-nO>m6|MU?7quNi z*WRoa%^~d@QvSmu%6j^5=uzPp{-aj9uB!NzPe3EIZTThGi+!t{Jt*GUPNE~=ONvSR zjY}iJQL|ePcdA|%hqcbC_=+oYXhup>c-hRwKk&fI*fNh2;(5Ne{>N6B0DnhumejYg zjVQaU0zy&M0Irja5f%R@?8dHS9p)p+1raNj&HXqP7o*OpcQkHKjJV3L1^iT*Z+iC| z5#1#9ihl48VEs*i!8^SU6sCTY-2QbvQ51h(m@LTo?)EX+bjHt+M1m2hg3QPBV4H4C z1nTHiX4li0=4T4n+gG^J@)LB9f=q^;@1|KpBm%%k$z3rQ6vJ^#Be;~66vO4ME8TDxZ2gGh7d z#XEZj|F^#xv6eXH%MkaX4k^XL_=Hy1LI}U5jpvpZ!vZ4073d@{do$ced7x;85q@ zM!q;d5j9+xc~K6p$?5TP(}JEX(X%c6dxuDhXJuVgG2WG!=D5ML&rWIB>1#W##a(|{ z{Y`hvl$~O0R1U=|^&Ea`wWi(7%R{}-vgjhKsKoc1yWFnpc&9la=@hb|*e=>qmfF`; zxc$U@d_s%boWx^K#za!do3Xisf;eO-_7J&%@bZcL-ecB?&D8$OKI7cXa*e79qYv_QMd*rT|TxCnu_i$JHjjQ(WmJ2m^%{*CgfOCDm>7bb6cJFpg z3doXcwu_S!L0dkzPdWDylM`Mx&?xA?7NOe}bV%0|uoU6fk=6f}vxFLB&VKU!UlgfY zd;m}}!hq@^f8?aYNWg%LaST0D_|AmriLk*sAJ5~*-Z_;3XkZDLZvx+7IhpbDKNw1C zh0qU&y^Bh09fNYo^{2icL5%$?vTD~(daXWi5qY9yPDqH491`Q^OFB{SxiX=pds!i2 zm%?r1WZckzIP*b4;{O?r$qpCmQi#FwMQcw~7e%D{mdDAHH?nuM0a`Cwnn9 zW%-)@AP_!~Y1}U6zkpa&Rm>BGP9=a6qoN!P_I3qQ1``hZTM*xbl>4lHbmGmsmy@+i z@s#wz5%2V=j$2{={KiNGv`oJAnmlbpmkn=b*ToPgnzEBjqd_AF2QyS#0QTU{fSkC= zzd?_Ue~Xib=1TD=OlwCGnDFYJ7WFb31k2446W)WtQOelBjEJRExGZ8MpwfOeUZwV^3=!#i!M4_BZC9 z+=ov3CgTNhpXz?6F4D>f{epGscy#pkI#}_R%$08TeQGxQG6;f2FpYkzWY)-~y>$nc zVb;_k;kq$toArimsohzCpAjDz4(2JETroCWdeRUb5>{x&M)*f-p;V0$nPFfd+E9tVJNA?K9?W1&fe5MLljoUKSws+kx*NBr9TZ?a1X^iD|25pO?Kvcb% zy;-O>nEun@Uuu!li$Ww}b?g{f$vWdv`~on5$wYkfio0@4Yq|Nv`>?R5;MK{GIYITx zm1wjVN!mLZhF^^f7lKdfWZ7SByEJ^>Fd z|BDw+g6F;Ch@ir4>g{2mPlzuY>4Uo@*ZZv2mS!tI;sQ__RkwFt)n0zS9f9gmd?}@i zbl0?rtOYKvOytfQx(rQXB{2vaoiWFt<_^Y+6;5vX=H-*khsl4z0Ds;DzQSjVGz6rR z?rb~S_S)@)7*wU^^2_lFEca2kkTgCs_5UPzYY`%dG~1b~E4Sdi7IDzd?fy`V7^{G8 zGhk0^-dWajT!LDItrDD0U9+KtVA`vaLxgZ3@};s!24TcDnPn&TNXbblg>o>5eu{Ir zOIpPt(6J@#LY)I<+?-l=0QuTIvgyo3fMJ7DikQ3AMlhm zcEUprWt@cO(;5e*iZwd}7X0{dmI)fn8=U&>R4gn$kFcDb{>aUxK}h&Z#5}jyhHfo& zzLCx-w&%?CYo3CI>Vx)vwpQzS)3pObuxT|09M!0xd9{|3_8)FCrjqR_h)^-trjTx% z8ri=#S$9vDuZMV`-+HpslbujEJ5Ive_{mg_BhDv&jcnFIa6|kmv2@e@) znP{|3-K?O-ak`4?eG5xUK*4YH7730>MCQ_^+vk3X}YP0qwkO zp->f&yq)#jy{K&L!=gxCP?AYJk8-|_zEh51&yhB2jj-7K7gVbo=JzHn;{`eWaqu(jyzLcB78(d9un8r)?ufqrY#E%SEu?f7Z)| zAkNG{>!#mSsMcAc>J-H23D~F{)%xWWu0Q4cdQ zV|KdHqc}p&9dCeEPEG=(9AwKq>GstTZU7kw^Ayf5y_k#^D1z(dbmJ#$d1FX#0p-%0 zKXMD{P2Frf4lW-Fs6bt2S)Z}2J_H+(-BORUy;BH}E4BvSZ;%W@nCCaM8WGq#k;2LzE~&B1x&`7F`~KX*FnKBY=#xoHGXhh~h~IM( zh6DY5>uk^M$UlQ{$go#Hm(1MXMmYm@th^C%%cwt@6{m0Le7q~PDw zTL@#br&uCIR$$9N12%Pu?0zc|OKCR0G!k~A#pfsI*%m$J9gJnrta_|Esp%2QuETqA z#`9F;b$%71U|(8?zqcofKC*Tlbr$BQtHjxXGH1@<&Ql>v-P>wJg@zyYlEWz}9rAj< zI;#12p6lK}fa0D6773n&DW?kW7M90BoQtU%QS!U0u;92NZtyYhgZ-d>!+bI>q~sl^ zi%3V3osuXocyRIECL{lq{I$V;_O%;^-{egIwL+KUJVk#B2$nJV_v*L z_BsfD%-vqsGOBiM`!Og>D^M{)H5hdxdv9c=?Xj(f^3biGw+l)-tp&l}n*MQo&TaXp z&z-(_=D)hAHeTOUMA-bvxNMp@0{g7bqbzO^_A;y^*BR^NUZTmot$?h2^fOo8sfOt z+fCi54iM41xi%SiGq=&8WSv3rVr6j_-I1UuoczzmVW+q)A+je~IMiG5VZGaLI_!-! z>jI%f0?peP2XaVRt^C!!9#`hS=nwQPtBG_MtXv3AQ>0?^P8^mn3S~fh$s(j$R0box zje_*Dtm>)YzxI(Xxla2uhAF0iBaid`;aOTORr`c2kg+@^sl~*lfc4~A`->m!7_mwV3|5qx>-D{7j}FIaNqk_cqpx636!yEhpD5Be`$$l~Jo+0NLZSb7;Qq6n9tbKz>f+m?^TezRQ2=^K{^24=YKRQD%K zC6kLKf~;nVK`{5UdmQ}j4ChhVkHA#;aj7a~uT$;9Q%7-8&o{zq_A&Vvigyth`oErL zD)yQBMMW+QAu6?d)OwwBg5JZDL$>a~{i_=pZ{h`qsT|TOSuHF%yr;=q*N|B_aJ{V1 zBA=YY!Hjj_!C~V+*GCihVLCV0b2`jr4!sTqn=B%ftJr< zmE#<3o&SyE{F8g7FsUi4k~{J4CqBN>X-TL7h;__Eji_DcZ)*fhGj#2%1~3(NK=IsM zn>#?oZc5fKWa72J-27hrii48|wOJNflC`DzZGSwXS>|PNjRsq(xB_0q=)3X{r)5x= zHht_%hN1flT6P+!jm9+zY8-%y=w}n53JKM5eYq*g<8|aFjxbTpxZNTq-ui}xE#3F8 z4VVT^HwGCgUSni`8m3#{>ty!l*d18lOB*gB4MrY`>sa_(J4=OPVD`4b+Q}3Ljg5Vo zwotH1r{fi?9@E~|)YUzD%{j;yx6`1bnU-$VH1z*2QfDVpFMmW-^43WZ z1@AnNw7Ev_dyy*Lvhgr-pU~{1|C+hMrTGB8_du&Nk5;OP=Ov8IY)*gJrYJxLEcd1MVBYc&E%S03Td@_0$lO|4(2z& z9B39&f@T&<&m(oqpFLRU=(RA48uD=5Wq>|E%%Ki6vhHO!vtD~N$`5n=1Ei4rN@5uE zBv*D3i0Y+V9X!JIGW5W;4B-coF}-`mWq1Ej3hr8ry1Pf{Y%i=+n z#S&ekSz;)~A*tpWtMSg(J^s9)*TD49?*E5q#08vsH<84~Zx5+4j2|zkk z#VT8D=aE_4Q4)D%6fLO&3)`$#)QF?BBa;d$PVgPa$IqQ+6#ev*z46meQI`rk*J*f3 zoR1yKkU90a)7Ls}I_wtnMFZ#LjE}_h0oj|WA0AOW#&wV1JoTZda%Z`h6>i7``mNsyzCX6AeRk15#E%_l}fuGBZU;pNKU z*w9jll3r>)DhH-b;#8s{=5`<0kXH-=`FM$Nkv)X-R8oCzRRgP~9!v-yUSdvarYGS+C_W7m(kV{&DK zk(>kw8(7)S8siS=EOx!~58s&ShPO(6Qv+-JMc=)> zP7CbTupt`X3+KG~KzpO@5_lBT>*&mRdC>56CNVuZr`iCe>51(gs0+v7_*jL+Y(56w z((&3fPz8r7rkiVkP1gTFq@+Nflcf<8~X{ zE;lH~tP)KItbv-(LuU{g1f*Nyp$JqLez8*u4=F<-EpgZsn5B^u7~r;NC+xt?W79EO z3C{3Jbzopi0l2A`sbs-%e5T$YI%!ecH!)rCY+zsfNZ;Ty4ZS<*h_oe%e9D zL8b&j)%hTbx;F6Xx=3nG9B(hWAbo7|7!lv0E%3IzTYpKvlm?AUyeuIDnxl7m`A@Hl zioNy#yc$qE!nD9q>^y+JTc&xG7@F^%?sZ77{s8IKlJBWZhL=GdECZ=5pKc~oL%sBO z3OfZPrTJ5kU9z|YG48V-7>I$9nL7}fgcA+Y_V;z#Bbv^UDM^l<#Zm%@JfMM+4@37h z`*NSACE=4^E2nSnC#N^v%*w_f$B+6zy(~LeHZdEj5jt4fQ1PLvz|8Wv%XqLk#|%cP zX>%}jmlv?oZ>0dM_uLeHCq8DWcFx`Ya!}~#qsVu_udurc4R{OjQy0j z0(*XaaaC}9Q?yip1<5W#7Fz>hkmYT^&)PZAxl4B13WVKdpRzqOly`n^!HR;tJvVzh z>mS~+!`_=u?W@Ui8NM6~e8_)AwznR(jTUa_QB{&yFFX{cbT%1&m7f0iHh+Jreii%R zT}4C9IiyLfB+X_U?^f$#_4ZW#Bo(wDq9mQ>f2(bKx!%_B^Z6ci7C%mxPdI1obUa1Y z+>cJ(QP_7rzNsz!!IgSx*8V=0Lo35_L!c{t>O`Nv$9WQRVG)6^t4mVd9@!YzMZGLs z2yT^k}B{=jk%allkY)YNK^Bmx5 z#JDJBn0P*J3$0C21UQ6Fye;qywgzUVMf@d)++3x1DWXf&O*-B#o@$U}9eX%6!H2OT zIemPcnl7?-ldTt5?59DEjH~(e6js-ptQE(r>nS!P7JhcSD|PVMP3m+n7GJJZMWe)R z+=)L$q4ZoPrNV3_GIz?%YEp&Cn=2l0xApV`St}w0;!ZTqt2{CJK&D8<+|X15sPhWr zqkl1ihW)x{)wV>iz%@3%?dJGc|1d{(1(9e5Nnmu2Etja9v%kG48v003XuoWH1sH{M zlAo_}4?~T=ro~OHHin!H`Gy6lj0gY#=C>w3t<&YnM4bV-;#Rb!4RchU5m}1(P^c|B z>SWM`sRNZVv^Z1WAqMsQlE&tnkbXBl{oWG&Zn-uq))}LPdoavM7avVb;u$1YpC{8N$W0&P z9DIyl0A_}Q-py`{%%rRIdQp+`P2%AVQO;^=mHegm%zlP|f1%{n)Dkifb|jhU z$}&9I+MYa#By45m%0b-MhEOU5GWyS4hm>=2Li8upaC~fj8RA^U@r*?7(Y15?*-+QA z`l`AwQQiDD4_v7`!$|caF@mLJNz93oYz=cRj=_%|2>va+)V8siPJ0S{I{{%Zm4cMJ z{$^WfQ@Ta-3gsPQM?#qqBZg~U9l|wP>hI8gx}fo`K!ooNK8i-Fn@b+gq$LjP?%7G> z3iisQ+eA`pBY>0hbyw=+AVZKSJO1_PCn1TeNpHL1?{-H4Q$7TsvYYhEdnNV$#`-SY z=!FBMp0f+@;RNSAkWeO9IAC5_N8aVUP2gMiFqMhCQaz6;$f}6AD4N&OwX~Q&O+HrM z5TqVE_RD{&^VGYX_g3G{Z?3*Xe2Q>Ryj=%Tu~PvGg%w2qOd%_z#!yINd=faNC{_&* zZfeL6bZv$oDW&vJi#<>Z?TcLt%bu#2l?0K{1qfHtlmgX-J>rlR+h(!R0BPW19yITNAY zV}5a5o82&~>Z`WeY;ckv>y)<5#rb^g2?+RT=$@N&b$pzWY=b)wQ|H)8-)yUf&*&(7;5G1TKbKFoK$u6m&sUgJe3-8AQmW&# zFuy-_Uv-Y;%De{YbpJ@co_FF5(G4@A3eD>(<-M;;mqddOn?Cux=YF4 zecUr66!7NUEdp7m-0*0P`TJb=e76fOy`veu<;0JTeVrDg8ow6s$y!I-MM~y(!TJaxMr{i&VxGO7ji0`$2908cR&#@+G{b9sL>d=dmLBE-`)@EICXl%H! zlptDopE`~mgagf7)wE{n7#qsF4yID&j%dxW9(K4lG}?x;4a58i%V#P(KHmCgdo=Xo zaYJGyYuA!!fw5>}0RtS#i6HUQZ3s<+BS40xF-ILE0Z;5Tq@l+cKK*Q#>C@9H1+)pr zq8iDJ4_#}W*327sE~)}OXZ|oQl$=8H&P}op3>acT)3uE(y&`Nds zkmV6`pKRTL*L&yGVZ3&-PIZ2neZl}cWC&4e#N^o|QMPXXD}R4|3fh8Ff%k&<##a;fY&F(fw?`T5%Tu7(VEM@=YM}VYF2fgm*4BZ zKOD7{HrV|3C%J{yP|xM%v(9(&tBk!Hp{v|8J{~hmt|xu4Yi>8eI$UZI5~_5YuCd9O zldjVTRO|JN{F<65ICG0va2B6PEqTVPnG9DJ&`VT?tud7YaIA3?#c6Ge#GtNfD zQ@z_YVf2xuf%9WPGd`t%KTbZOf1f62GUG&(-Eg(s6S*zAtiK^^zJhZS<2YqV8zEkq zIk@)<(=7c}_K`yS;CQ6=Rd~lO4L~hG!~`YEJ^pFpkn|nxg~PvrY2m2|Ky&B@6}9Yp zS;I!p0c~&+afeP;P&Yr*DprEfu1U({-nNdt=}E&X)3{u7^8^0Yagy(q-V>9@b(LKq z;e`n1984$(|BpTGFmOmD-u`#sn#(ZbiTT+5racfze*zzN&+-tN0P?+pwf48(mHO|( z1Nv3?`Mcg!ZQBU0ytFBO;9bt#Zd_ml!%n*y(Zj*{p~!ps2ys4kwfbE$^77CTmwzIu z=TJpi1pWLnr)@LemNG}aln_d;T1e=LDiC*yGFyM4O$d0OAPtyD;2NTu;%>VRU~;q_ z%s*XX^QA}FaHeOiKUYQbO1Hp3=fPWP1?_2%op;1^9lnfM!=6o88xSP=d_WoD)b$;S ziw`S26H5<=B=_(7hRT6rHiL}dm#e>ZI4de9{HQ3K7TY*%;Zk`%W;hnXy3$Vl2Lf?!Mp@-p~*ou=$%XH4ebB|m7!Brj8~SdDEEEBg@geIqxd(sy$S z!&OPDnN;(bGN3Tr(P3Z{k$^sZ<%cErKQQ@04XGMj`~ii5?uYZ=2Vv@u`|Qvle<$!c zo8FN2XiRm4`hVuN{jV@D?ld}3WL`ZqBz_ZsGEF$z%#O2-@aH$&yWiM zTuc40x5KB|DdJSjmJk{zI5=?D9qv|+g0~x=Dmc^7WP;eL-l0q`R%E0%5es(r!IKlf z{tYro^K35&DoDoCJ5b^9`(Bu7{Md_P5wFM+9xI+9)64ddOUyXErQx^a49@DTS~ydO z*Pj?TMM{US80n!%{OLEU-6i=fp%*HT2XNl%Ro5k9&mbwX>7`D8us8yv+V{lwlN5?u zSKT22BNU8d0FJN|&oGVdl|C1%%z0VWIqn^F^L0dqFdl6Ss@b&%KG9V=i1a@Qw{kMx*wO_EtdqcZ*BH|8h^o`7De@zdbZ>#@GA31*{Jd^ zbeS-MjI%xu_#;b3KfUKzUCoQ3aG`ZUTn+3$Bqx!?&u%nY#q!2oqGirzg$9=6I32e` zK>_4xaJfQsIvf8XZO|fOMtu*~JMDC$xPS+nz|nFnw4j6u{YzzYH4m;hBMT(?vJd7e zURe)gkx~PcbAKrM-!2l-nMq!g&+^^?hzyD(oZbNJRXLo0uJg4vnx2j8$g#Zd8*+Z< z!{>Oo7(%ffjjUyVAoA+37Bn0Lkl5Dr2tnnn8me(14)n|*)oAy1Bur9GYFl;TUXB3g zJQZSlood&p*f_fz6nMdx;8j~MXQhAKQ^vnYYUOB%AVzZigqm8MO=eHZYrQH)X zocSwFKV#Z_;lK{T$|Ua!6C#D9Upc5pORrU&RGj(^PMbrXpz{HM>l;xPA52UOfajAN z--JH>nHsO(&yhGJ$weLFKxX~J8;`jrEjr5<&e`Fjqw@?7;!8gu zu@zhzysTESeS&wBL+-vqk211M*-i7OAHXmKiIX|_m-d)Lb-M|ob=yQyAN-VoSNzr^ z2j2AizTbZz4***GokQ16fsMx-xi~Oi`Yv$a{9_~>#kq`?2(4CjfB#@Rexge}WCVdh zM{<4FS>nyQBc7Xk38|qAceUDUMyhqOa=vK{@oPG0)-KzKhcL)k{zn1qDH%__$1iFf z45wEi>#oY%8j#{-{9g-+F4HWah}B=rP9do@NKxk`%Bm|E#9;u~zyb+>L0Y!JwJ&xl)#WfE4Qmy^a)=bHeb+0I)SWU`MLFzZm) zIZ;ax%rNURuKyf$!EHKWERO=+Xhtuy@ZomC3|j8t0VF+i z@Xsb_$~CD;yM-bX&)U>4v4i@dH=BJOX}Quf>B62jl{P*7(|A4-^oV-5s@A*w?lQmH z-PXI}?q(-A6Eyg5cQ{Ag5%bCJ;_-O#<~~_Y;>C{_%O~-h(~HxaLY3T@zH_Q-T<(tl zb^hjlGCNg;yeer{b+Ku840_28$ESs=TG4CFF~W}B_yJ_08oH&O@82{}sLtZYbh%6E zxADoJqJ<1Ce6)K51k0bZciApq-DR=7jBamn8<0s#YPH)tzO-;n-;#@_467m1&j+F9Zv(D2$m|uUmi0JmI+`ovR zq~zI^JjAgLa$o;k660Mbi>LPHpXi#S8l{@O@}6kx;DHurpSn^0=~tx}f84G5r?%GB z>x+SB?0L)L3_0kdXt|%QI4XsElFDxy2pW?qKaHaHx}qsu0c3D(>#Z`b$oDY{{KPF^ zUWInAUW(vBD3Ak7%EeSk`WXqY*1`RpN7-QWj>kP2w&&oc(Ov=J%81L5>g2#B?*WiN zj^u_99KKDjs=KeVve*o!6yeq03mM<-&Iii=vs+|j!i~j}o3#OKZ7vu*Bx?L$)Iri* z^Sb?v&*s3B10e+6m1DdA&ECq^?Z-NW1-;iBGfsBh9y~z(|Kp{>p0@uDJkV4m1APST zXdB_B+Cm_C?Qkp(Lt8;wERpj0$k#t1`~Z)sWVgb*mf_OMpyOjckStXhsv3qbIH=_%tDWYRv z_-GyE_@ICR=i#Dtg70Y6F2-dmK@+)zSAgTkoKP-^vxAdoZh;comNt-&&S~HH=;;NG zHKMx$aF5pDdhi#8z3tCoD|6^|KLxu;&pbrJeXEyNho$EqWY4<8VR9j3VDl7^{*)~O z$y51i7t*?eYJV@6utIjuj-oi6;~%p^@`2Z0+ao#M(DU!}2^As_jRj%e<1LaOl@qSE z9;ThC4(|?9X>JdiJZzu?A8VNp>gfx4daaLDp#6!V+U7z-)I*9Kvc7F@Blcn1`A^Q( zRSJ8Ox(R_*u2>?z+3iN-^NVyfV7O;s)R_okM^Iwp#)QsB(3XdbcB330QuR)`?Kk zJ9e12yqvRLn6W^lOs3ZS$qidYw`sL7uRA&KeyH@ZA`XbWFlV#I_NU^e^dw~(k0e5z z$jAarQa2>d?2C8V8n?WS2zg)dG1I1A(N_3VPF23E*HQp8p?ea|yKTU-t91)tmrf=) z7c~4RtmJi-MDi;#ViSt=&q(=;Er~=@F{Pif1QD?y5gD!n^Hbz;pn)_0j&;O4#%G26 zK|t6GJL-U;G6|4V!3--?IC)jxikB(JY(I>FO%l^)id-hQF2F>LrH4$Vb;sz@-#t5! z3o4_{A;?mqwy8ITd52hZvQ!T^Cd?=!(u=?CpN9p45NqKY#M`<_LC_d5$OW-JC9o^R zSoq%qwXt3b#4~I0jBpdnTB~+DdAb2Agfw`dp_`x~KDNFAC&*kT_cUB$${MB4SeRJzcX2c61rJj1sdFDQ}@-3VS5t<$kuR3z4L?P-J zskW<37C58qJb}5h@O-z6mCiiPFvut8JopB2fRh|^q>s0@iq6_9^a8l%wQ&+Q?Q|E_y8puVoKmyPqMo<81@5DWO1kdn3dkFg$K`Vs4js zqm*>O<>I+@qtgg2&u!uZ4FgrD`c&p^{+`5b9Zt-aa%Ik^HMcmM0i6vDGI%|hYDOeI z@3<0_G_*+=jS4IR6C?z%c+(e~b-)$vb;%2pjAQX4$UtDWwFBD2&)LKHaZ9&}1+a7O zpTBq=aR9vd>gu^wS@XVJB_wrd1O@sPwYPP3-$V%?5&MSGP1`}d@Bw}Vi6ajDc0)ma z-X^{VPmk0QpfZNjHnlcy69>Ngrize)yX-+f2;Xbl^XoP^pkSA?vBu8n;$}#sZ9Iui zqlxw!^;)`Zofft=s*pt9*xhk%oZ5)5Bz>4A{RY3)tJXJYTfSVoLw(so1i%pxPJsZV z028M}L)}gY@qE7h$Ke^o7@{tp@?IM_ids2cWh%Z?!{l z*BS94B0NN!fEjoa0kMcryv@-I$#3(1k*&QL&NRbWV}hmB#&dgMmctDt@zTn_5Bl96 z06m;(XZe)f@hYRk3!Tnei%w&4YLc<{1a;UzL^~)^8^5c)+WpITADCzF>ul3KOXU^w zPvEqA(7C#~5H=S8k%_a z)pR1(NsLgh)Sca1bA4Gi0$?Jiw(62kqDrEy9q)kS`RHV|$g^cU#i7pQ}wz{4bZ@k z<7jd@iDDcseMEw5LHoh8f!p)Z<{@5WC%^vNa$@Se_3JGrC(Ah1)pdn$+Vt_WZ$JNm zv`(b)BR%p9DzQoXB9pZ*QtyhUxGdI1`#ys@=jFd?MXdiw>4--2lRaHZGC1>iI zXyYG}5Al<;v%UJcO&8JY3Q%2~LyrD_R<&()M?Vk~7cJH2sPYd^Vn~JG(*IvqzXBhu ziIz^r__VbkgRS_6T4YYc@(|BR)y{z6{2dXX=6Mqn3W`MPDFXdI@8G@^g8S0a4cba3 zrRu)Ho;Ta>yl6YdU*hI)=Fo7d4@ln!=|>M5Xle98MbYeqN1f2lG2HDq@3V-D#?r zmx;_s?afH69X%TkhdwYat@n=JcSHlF0|U{{^=JW=8EwUZx0bg-u6iivzhU?9SWu8h z5TB#{6AbK}iT!@g28qJCj~4o~s|6I{r6pTj!Q@!mj#frn3xO&hzi&cpzTS#}T=4Z|{G**mt-=*sqNNvBkb&ZDGc98+rd8%FO=9#_PW?@9&{ zfK&B#v0i_x-!%%jEnTf?N}k)R%2liC4awW=oqb>283)qd$<0r-9>2l&FTXo9=wiScWZg7YpUR%(1G+i{`WZ#UDKxu4lP~R&3aY7+7 zD&-`NHV=^>CDqB{4DKUr@{GMaEzpw7;fy_MjIT{;WN!1iU_+xrqHXx&8jD0rL)D2h z=ZA41>_RPsnm=rL?2y4fG5MNYehp7b-AuZSy#p}U1FR)Dd_*w^d3Y^}3zv2^gN z(2_MZQknzYI7DO21$>ZxW1WYNeaj!XG zeSV&txuca&IFV%fG>$~j*IM_unb?iCoc!D}Cf^ymo7VHOZQ4{mal)j|%^iggnbt@B z2Xud>{v_f-jP!G)C3_tiY}=KNdOiPe+m`CwxW4Sn6>6sb*yVnH@5EfmhDIdyT%V>m z7H6|{)6#Ys%meY;R*AseD?X+v|o`7!M;Qr0CK32qXzClQj zpBtb|zt*V{oydHA%xJ=GO8-OEjs8?HobnO>D=$v}aQ25IS(f-EsqXRf^dgFRlp~#} zJhGy-B#A#76?Yhp41G3sjn@?nMj`SF5EsT@p!x zF+rj}Pt&jy;e0M?6Q;3l8R&{QN7ST;#^NB#M;YyYN%!D$$!% z-+jW4XgPMR){f~pQN`NM$WdJ##DX8h6aR!4I;Ix0Yqkm7H z@?RJ2LDHbQZ(Z==xYIWX-|4rlL2Lg$cNvDN1K9LDikc)EHqe`46)4@C!im?4ez$A#O?txfF}BA)3?%t2yf z@tGrr^CWA$(2 zc{?`H4=plf(cZ8y$Lq^9FIP0K-~H_?SU^c^;eB@!rO{;I4pJqv6*pAm@JbwezslPd zadC{<>RnG;#E76f8N-~Kag|&q61%EVQeUn~XVNIoIeb`{<>da}S&S|1h|}*oIS12T zEqt&k#^s-I><{cQ)q0Q_wZ<1d_LJ1n;7@4eo!;}4)lp2!xUR)d_bo`nOTsP*d>>f~ zoWl{Ewoz@OIg??aUeR~jZ#B=RFm=AT^*uJLV z=3r`<7Ij}E0Ja=4UdxdNaXB*REl0rLIwFom?BYnE@s0$P>xl8bj;=1(@`HhKfTJS6 zwj22ivFpfU=^PpOoFfOdbL8N4fWTPZ(2FEGN0=v&7`a{gyFo@~{GjIZx~}RYFGEI% zYuIL4ZCM7J(Ht6o;&TDTpBbf4f@db6JX!s13v&v;l3Dxfct2iN5cvFGDK(y zbM)LA_z{5)IX0kLEQQ~cGCH&`ZN9g*D1qh(68}ge!Pr-N*6yKFQ1&Mzw*90 zeKMzA|L9G-d@^4A2v)g#Lfh)io%%1muV$A-Z#z;M@c&8ERy8)^>EwhbuAwjDy%^i_ zjx+%vt|auv|JINH`HR=P=U;sKInt_-tabY1o6|R^r>ZSE#mbv|YG^r0IL!*(Kp#%$ z|C&hRm1O+p3W-;C@u@12_K3&@Ye&^3i!=OhQ>M|A)5yRo!Zl&`FL22J4k*LO>7>2r zGa7U%tGfO*8Ik*UE!TuQ8dcF(`|sPdEyXhPT_>@TWIVNuZR-n<9_uO(q7wnxh5M=v zW|Au{d`XyIwHv%y#wLxeNt{O97ROd3mjQUFw#SF;u%RSVTFx@uVi`}jrJ&iDFsXFz zX($HIzhvEm_G5t(aT!(V2BH~fa(`<@L)bh=XGYM?*(ACMv;+OfI?W`FfIVR74&9oh z&fTt9te5j@J{oV+wI}>1P|>wRSYyAA_QnSP0F*CQTbl&YuMEEkoWm_>n3l)K4HJ?3 z^i?mfU1ul0UT5vMk~EP4x$SP*#y~0VfGds*Dx2p?jUzXt&xrx#iHdGF_0aZc=n#sl z^pP9rzh`tE#G5%jE=yzVDa&CfC_j2Zq7-oQ>SaZCx!NTsLT$s+GfoVzFV5JM-IbAg z07eOpB?rfd<=QI5tefb48XH#p-N2ulszW9CJy1PFQ?MaUtoT+uz zv|m?OoRAgh&Uu^NaOx?K#uJ_d+GafO%c6Nvt+I9UUs^Kz9{z!`^}Ga>QiYzggDhQY z-%Ja;B(n&*ELIFAc=~X1e}8{M%jfQdUMI5h)r^6qp+J83`pc95h?Mq3iMI@oRw8JmbfGLH<|BGSSo zR34TxMh6OB(he(N60~{NWz2=I>#S^Ga6)0F3p=mvL$9&6loLDhpwe1+wh%8Ka1B)} z$H2E6cvZHX9m8NE?RG15?JhZIj|;rmXx$1Hsg1>;RkzgU=lL~B)c~4R0MmPxt=BlF zr2T|s69BQ_RW&?QZjghh&Dw4AOrw~nJ8Y-#@y|Pw1TND^5Z*~+SFh7YWFLG_oTJ5Z zB?;?qnl_$OdB?d<{1RzHcuG6-JISC@ffv$PXb9=_uQtqpJt%s?js9c0CApwA)>JXezXWqayqb-qL<+zLRYSx3DYS z6*NAt-IkSJ!P8jLYh?pTU-K481+4nf<#krjVwlpJPsd|=9sk_zzH4LT0JeJkpE-QY zUgo7@PCtoGPa~wp65sIYUEbbSaR1d&6DwOHRj8(wxS(~{#ogEyiBs`m`4@@Lgz9Kv z))nn4)gAGAc9%9nI`_XLr>mfPIvS6%-3|dVM2Y+v$cgh*UmE3PDn6RDE zf43&X8oOYtI_1F+YVqRIpQZ0l((&K*lXX0gj%ldoQ9RLKslDvwif0a?A_ntP2=y=S z@!FE)Ux-3KI{zt+CO;r%KB*^zuZe68G+taAp@&Zfd?|?ZpyT7$(=LA+M_*l^m^vq~ z3gXZtT6CXe8H+@y4>j@)EyR^y>@E^*C3 z2NMU!H5e-mgzP5qK!2Iu4O$+=?oNq-HaP0si6YkL29Wjax&Ylts;fyR%or)~#N&#^48I}4B_Y!}qC zB}kJcWsExD)T?a^WoX=_^!=};z&{Hxy^eHcz_{5bV*w|4M{fn7C&~SH>TRqoBF{uT z;GHbyD5;}ov$x}ubM=gL1>V2-D*C26vXWWk1yH>e?*psABm1nnRQKvx+Rl(?{2lfC zUHTMWOzhZYy3Zc@&g-XucTYs3jz3QM#o7E5s3-I*eR@Vi@bTH%1#J``pM8S!Vvc~j zOgAKa(v-r*<}$s;#U)*m#=oAAJB$3D_Pn0-G>%@-`nAiNxZwQ|KS=NC#_;o4zV^1X6Yz>8Z2PgBZOVfpQTY*8OMl3e~*;5ccvrB_*r_%G6RV=^ov>Kd-}ojN5e9D z56jK_^qIouJ$i;?8)_pL-I0chA=u8a4H?-Z&FxR<8k}MD8FqUbty}1@)%u-83vATl zPCZkvm&l1)=a9Hf`D-eFzjTd4P8#1ptPS#~ITByb14TCRRb0?Kz5Fggi&uy-$-Vb1 zIaqVtSuIXk)+^@Z#B6a4t3I^Bk*4iMz`tgZ6=yq)D4 zFL$DgIt?<5!=q0{NXyaJ<-3Mx?I2M@dR$*jn9< zjXWIZt~Frx)w8@^D;LDQagGIx+SX>vth*Vx=s_cNVvPd#U-Jz$-xZ<9%WbV+q#eO#0>c~u|XfFyzH^z-6G^j$i*ucXn zE#f@~cN;YCqiQ~`M)K{*lV&=CFk^)0dR_`HjPr0vJ5<4yKfv13CYu}#Ee_4z>vOzb z^isqJ-_$H;71U3e8k1!gL6#Qv zE8aj&69|2Gt?*Thw1@C`oi`l3ncyGJNylNLr~_3=PbylCJql~MCSFfFUA{0O$IG}) zo3S}l#6~RFdjH+n<8J2iNyJ8(S~GAFGnzSR7qtJ%6gEsXr0an$=~LPYqp;ua!*&{D zGStS=ePHjf_A_Bk+EC`KT!#t&d-ITT1d+-tz@YfK)uu@O%>s%tAOt z8Ke=sgKedo$oDzu`TDn0t~Aoh=vAqmXfW`kV$#1P*YprgwKg{z2ilrsVYjC&zi9!7 z*N%H3E62yj5&nATGt{Vj1_!+dI@h=`-8X~Tac2o{BA_}b7$Y<`C{fwG#kn5-v0C_5 z2`xOD6t=n{U1*cXI9FC5&mGg!A4J46Fed1H^1 z($!c(vzxSXTxstimLz^@=kW32w)VS@O~x1v^ZTRkzkcz&Z8utN*85HlJUL#RH)Z!M zlbR#;a95^}XUqf6pFaJVetfc5?}2Ra4rlJOeEKO%+uw^*k;Xg*^AYEk$agKm_RI?w zTlBj!eh17a5Z0O&Q%eN|s! zhoPrMm~z64U&u)(y#gerbRskI$9ApTLuF(4NM~2u?aD>;{fVZE{OW|^xi}(gs635A zGK%7vCp|ZPyG%hMFQ7Qvp%Y1;Siwof==TmnvLf+_$cWFl@k0#Nyi;j=3@gfwvC|~p z)-@+-SSzHv9qnMM@f6)SsmFKa!W7Y$nx8?nex{BlIM|&FQg2we_rjMr$fgvVBmXtZ<)cxVZ8v#f6%@%@f*n7Vs!Td3G*fVFC~nyNjtYBPHM#R z{{*qyra>L=Aj!I+nPjlbrB68qnRs+VFB08DlsvjrtZ8$A`lR0N&SzKEQO1U==^rE2 zoTiyF;b*2@)l4d*V`bDFDJS~dPHUYCgQSC#ldUyO6s)I|pvk&TCq&A4*O@8--On*H z0BuMSsX1>ZLBwVV}ee7Balz`#w`)2nUGg4&4?>ZvQiELG|GNzDKT z+jKo`LDNkz3|k$zZf=q~KCN-o?c_Kb`?k1#&qlCZE!MQ(lr*U47evC_d6|r}z)puM zOL1=krkgzol-%OE&L8eW2w^Qj{qsGH9yK!A7g>YQ0}>Bt=xsVy9kp5HP14#z<1$bx zJ3fgWK{F^xX~##&7bmoUyd|IbGPem=c$gY?um;$=`C+nQ(Gx^i+49PU@@RjJk;cP;Z8%q6iV|I*}s;76_> z_-LUXTk_c|Lu*-C+$C$p8$qI5;EO$uEb(DCa6mTiGS}L5OPBH)wf=2RSYke^HZ}0u zE_hugS02myyz8_Fxozh8vGd6J5!@w1OaN{SS^@1{&N00bLu*CcR^Lo3me@;49xCpW zl{vsfg-&31Es@-!rgAVhc9ur#tuPYWXX~ZwW)(|s;p`x9h2K4X6|o%9pu=ggf0%ws zgU<`i;f4K-M9U!1o@+%HdrAAx2m?>pug{v$j0+7%5rwcU1_`ue*HPN+(KG&+H%k6x zZDy^&@egE>AyH-iwzL)VIC*PRngmAaPlt`tY%gW+#TCq^8xyjst^+8C7aWOutKnjD z6GrQduz#Phz zOeRG3{cUWh%sq5+LS)K6O;_~dbUdz;Av=tyn60= z*l@kh`*+U$J3sh$o-R#h#adi?Iq;hK#C>V~s31`26|7lE1=Kkftr>oSff~lwbM!ig zsfa00yYAvQkk5C!5l!4Jmb7P|jA1$|%56Torp;%+!tLAR<6HLDc8CXc3`;Zz{Ct6l z*8x8(ddcnF3>JL>kg>1RTOEZMC$}C3qFoaTc%d+wn|kT!I#$;bg{eVi)*Y8ypq=Fo zb4K;=ixU3aL9M$bXg#S`u@BC(LfHA<8~;_@q$G}B&++*QZQagloME~`|_er=mO8{2@LI_|LU$~sL?hp=!l^3N;P(Ce4Huvi7wE&a8D3xC?eWaNr z4RZ@9#e{C+xq*zws?vEiu&p(jc+R~Fu!}P zZLwk{jnKf<@>6t+j$&0h#cHqUxxN8bzHOs}dQB&7P-w2R%M0wJPD|rIW6_J<{k_b&?L6FmC=;bg)c@g`(MJd^Ylx$C3p9CG4s8&)rqXaPRPZ{ZPp*C=4o@HOyl6Gthe0n>XJ!Kv z_)Gg%3x?M2ZTz0TcL)b6?QXW5uHXU6T7IQZkw_2d?ttmfr3`?tmgCWQHZCmiF$am0 z08liy5fHj)ZIk{Ayw|;jshP_Cv;lH`C4wNkws~l(W^_eL)Rh!iZKw|iC^rM{$o%@5 zQB(LzA$rX8W+(zlM*www^Cfd3j)uIRfnrF2Rg2|;)aKF;#^2FvmfD#_PFjz6@L4@= z7|hvjE27Icd|ZQnx%bD%^|)a>br|y7^ea9q4SbG!NUQ-aE5gaiEUxefR@_L^fr)XJ zd&;-CuOnU^V=e%J$-I`Z{sQcWRj*Y(F4Q>*p778bl?+3EmCHa$#wEs$mFXYMT58VT z@>CD(henn5S8udn*;<&9jW{#rA{^*tIMVgJc4iY+UJYS#;Gf@Zo?cjT^~lO!4-=R1 zjNP!dGOVo(tIo29(QKEN+yw}<^+yw}*pX~`Bu#SkMkhx{PIQ`0JSUwEE?kddL-XdN zI-XL`JMaOIB2Omy-liyP1!8OV$we_MCh60YmQ?1SIg-3y7#yq#>CSXz0j=oLPjfu3 zr!Q~JLy2O&0g!M{T=lEcVM0n9 zdoKTW+j=*+CApy+a`$v|znuv;@3U;fiEaF}91DV(4&%&W=KJA5^N_6qj8Z%50&Nx9 zhy>NnGk_2yaW+fhR$>N~s;x#*@FE+j-16Ys3Wh058`bso{48;Zh$c<1lSJLm#Qg;0 z5oraV+swV+3s;L%;F@xm{6nYeu8kR^c00itnmhl(!-+MPk*ZlzL}o^ z^TS_nZ7AJzk3Z>h2=pRtu*UKT)jk`91E=M}y3D}6=^sznAit_eP<+BaUw##zK4m#Z zm=JtSFf?gT+%-1K)!XhqjAVC~iIsY(y5o_Z?imObI2qJlFP&x#uCv(z$%Q95MoUyQZ%AgTnCnDJEs7=(A(Iz9hbx!eBtT>>KQ zvV_a{SGw}p4n){UY+6NM{WjQR+dh3s8`Pz^4Vn5(g(R4+tNY~N&(4S;G%Y&~SmlAS zw5<>h;=%#3^VOZiC(Ad0s;fR@Eh$ z>I2p5hM|Fd%fI~8h%=KErZBk%6^FaHdh$tsUF8ZQHh!C$??dPRBMowr$(# z*tXNj?y9f;`oDMY^LL%y=d0#D=U8KoOYYx&<^}~}xs3s?FD)T#8ZVN^3{~*T$Pkht zj*}rRFy#G#d#h+J%d3H{{NyxF-qerF(GGbzojOSzjry-`>F@Cc?$=X^(e(4zz@ z8v-qtea+RL*MD(RF}itP7u;n2aMYm3t+DDSVE+Pydde#xxWjv-KvBMp zMB`s+og(AesPe{L_~s56fPWX9OS~G>HFnx(;SZDBq@yuKVlM{9;`j3D0~u9%j@P{f zFZ5ID>}JB!*!Vg*$G?SS>7Niy5m6bjaU&9BL8g=3?*e#5_@7b86fN?!+Kp zRGp;53-Fw@eo(-QuafBkx|juF@(#t<_mjGv`|OsV5>3fI`}5n|-{7Jey7F0eAhzd- z49;^rafx||`KT4Al_NI05UQ3s&{pgfgF~&6W}zQ`8T7a-+FN)c$8n4D1ymgD$QRTy z2~un>hHbZIk{b%kCFf+|XD0wAp_)^>D_0(5P$eNBNm?Sx1arn3S2H7rde~u68+4!M zR#KRc=MaJ~pZ+j1g*{xV!<<*(JC_m!LLEq8t4;B3A2r1Dsb*bw!4UN1)X}pFDaa?j zcn2()mwvm0bu0GL>xQ+jMCG|##yQ_^^h;O!oocCn{-SW^k`KB!{$7^TDOxD*;EX}> zoAp{GEnAmE+~46DG_Ky>h!?z8Luc@Fv6IiV0gAgrmu(_W&?7iwkF2tl9@A&8No`J5 z$wzfB2*pDXQv+m9({u*XmS-Of9w(|3>BqO2ksQK=Bk%9(m3N@YUuLk3>}`#%&r6ad z`Pg2k3DE1(ALF*o8-H7VNidz%Z9tURT{lfIt*S6a%x@(9j`T4nl+cA~v+MmWoB|{^ zim~v@jn<_~Yki+QtJG2&Qb^!r+y2}%ui&(d5@QYhp2@Yf;65?1du(5AAL}(ex0W#e zl`noJU=@A!wi&)|Il`pzrxAXVTuBb@uUvBvz{x8Y3K~R43D4ua4VAmgrs6XPW zBeP3!%sCSUnhht;zqoK)k60kp>&5fqw{JbH0m1F{Pkqy?tHlqqpC5p6kqoWwAyuxC zn%~=pPN18M+w1=2>L0&)tSfnFX=&(R6qOiWkJszt!hY3n&j4>K{hX+Tv0KIA`-rxK zqqC>JGQZ??m0{^hF-z{y%uM zHIWNyz!ryMtf^^{_s7(e27J@r;~>s~of!v#)7q5WS5VNSu$1=l$nP+!+2Ej-lS3R2 zcwmtU2l-8�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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/openpype/hosts/aftereffects/api/extension/CSXS/manifest.xml b/openpype/hosts/aftereffects/api/extension/CSXS/manifest.xml new file mode 100644 index 00000000000..24800898252 --- /dev/null +++ b/openpype/hosts/aftereffects/api/extension/CSXS/manifest.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ./index.html + ./jsx/hostscript.jsx + + + true + + + Panel +

OpenPype + + + 200 + 100 + + + + + + ./icons/iconNormal.png + ./icons/iconRollover.png + ./icons/iconDisabled.png + ./icons/iconDarkNormal.png + ./icons/iconDarkRollover.png + + + + + + \ No newline at end of file diff --git a/openpype/hosts/aftereffects/api/extension/css/boilerplate.css b/openpype/hosts/aftereffects/api/extension/css/boilerplate.css new file mode 100644 index 00000000000..d208999b8ac --- /dev/null +++ b/openpype/hosts/aftereffects/api/extension/css/boilerplate.css @@ -0,0 +1,327 @@ +/* + * HTML5 ✰ Boilerplate + * + * What follows is the result of much research on cross-browser styling. + * Credit left inline and big thanks to Nicolas Gallagher, Jonathan Neal, + * Kroc Camen, and the H5BP dev community and team. + * + * Detailed information about this CSS: h5bp.com/css + * + * ==|== normalize ========================================================== + */ + + +/* ============================================================================= + HTML5 display definitions + ========================================================================== */ + +article, aside, details, figcaption, figure, footer, header, hgroup, nav, section { display: block; } +audio, canvas, video { display: inline-block; *display: inline; *zoom: 1; } +audio:not([controls]) { display: none; } +[hidden] { display: none; } + + +/* ============================================================================= + Base + ========================================================================== */ + +/* + * 1. Correct text resizing oddly in IE6/7 when body font-size is set using em units + * 2. Force vertical scrollbar in non-IE + * 3. Prevent iOS text size adjust on device orientation change, without disabling user zoom: h5bp.com/g + */ + +html { font-size: 100%; overflow-y: scroll; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; } + +body { margin: 0; font-size: 100%; line-height: 1.231; } + +body, button, input, select, textarea { font-family: helvetica, arial,"lucida grande", verdana, "メイリオ", "MS Pゴシック", sans-serif; color: #222; } +/* + * Remove text-shadow in selection highlight: h5bp.com/i + * These selection declarations have to be separate + * Also: hot pink! (or customize the background color to match your design) + */ + +::selection { text-shadow: none; background-color: highlight; color: highlighttext; } + + +/* ============================================================================= + Links + ========================================================================== */ + +a { color: #00e; } +a:visited { color: #551a8b; } +a:hover { color: #06e; } +a:focus { outline: thin dotted; } + +/* Improve readability when focused and hovered in all browsers: h5bp.com/h */ +a:hover, a:active { outline: 0; } + + +/* ============================================================================= + Typography + ========================================================================== */ + +abbr[title] { border-bottom: 1px dotted; } + +b, strong { font-weight: bold; } + +blockquote { margin: 1em 40px; } + +dfn { font-style: italic; } + +hr { display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0; } + +ins { background: #ff9; color: #000; text-decoration: none; } + +mark { background: #ff0; color: #000; font-style: italic; font-weight: bold; } + +/* Redeclare monospace font family: h5bp.com/j */ +pre, code, kbd, samp { font-family: monospace, serif; _font-family: 'courier new', monospace; font-size: 1em; } + +/* Improve readability of pre-formatted text in all browsers */ +pre { white-space: pre; white-space: pre-wrap; word-wrap: break-word; } + +q { quotes: none; } +q:before, q:after { content: ""; content: none; } + +small { font-size: 85%; } + +/* Position subscript and superscript content without affecting line-height: h5bp.com/k */ +sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } +sup { top: -0.5em; } +sub { bottom: -0.25em; } + + +/* ============================================================================= + Lists + ========================================================================== */ + +ul, ol { margin: 1em 0; padding: 0 0 0 40px; } +dd { margin: 0 0 0 40px; } +nav ul, nav ol { list-style: none; list-style-image: none; margin: 0; padding: 0; } + + +/* ============================================================================= + Embedded content + ========================================================================== */ + +/* + * 1. Improve image quality when scaled in IE7: h5bp.com/d + * 2. Remove the gap between images and borders on image containers: h5bp.com/e + */ + +img { border: 0; -ms-interpolation-mode: bicubic; vertical-align: middle; } + +/* + * Correct overflow not hidden in IE9 + */ + +svg:not(:root) { overflow: hidden; } + + +/* ============================================================================= + Figures + ========================================================================== */ + +figure { margin: 0; } + + +/* ============================================================================= + Forms + ========================================================================== */ + +form { margin: 0; } +fieldset { border: 0; margin: 0; padding: 0; } + +/* Indicate that 'label' will shift focus to the associated form element */ +label { cursor: pointer; } + +/* + * 1. Correct color not inheriting in IE6/7/8/9 + * 2. Correct alignment displayed oddly in IE6/7 + */ + +legend { border: 0; *margin-left: -7px; padding: 0; } + +/* + * 1. Correct font-size not inheriting in all browsers + * 2. Remove margins in FF3/4 S5 Chrome + * 3. Define consistent vertical alignment display in all browsers + */ + +button, input, select, textarea { font-size: 100%; margin: 0; vertical-align: baseline; *vertical-align: middle; } + +/* + * 1. Define line-height as normal to match FF3/4 (set using !important in the UA stylesheet) + */ + +button, input { line-height: normal; } + +/* + * 1. Display hand cursor for clickable form elements + * 2. Allow styling of clickable form elements in iOS + * 3. Correct inner spacing displayed oddly in IE7 (doesn't effect IE6) + */ + +button, input[type="button"], input[type="reset"], input[type="submit"] { cursor: pointer; -webkit-appearance: button; *overflow: visible; } + +/* + * Consistent box sizing and appearance + */ + +input[type="checkbox"], input[type="radio"] { box-sizing: border-box; padding: 0; } +input[type="search"] { -webkit-appearance: textfield; -moz-box-sizing: content-box; -webkit-box-sizing: content-box; box-sizing: content-box; } +input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; } + +/* + * Remove inner padding and border in FF3/4: h5bp.com/l + */ + +button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; } + +/* + * 1. Remove default vertical scrollbar in IE6/7/8/9 + * 2. Allow only vertical resizing + */ + +textarea { overflow: auto; vertical-align: top; resize: vertical; } + +/* Colors for form validity */ +input:valid, textarea:valid { } +input:invalid, textarea:invalid { background-color: #f0dddd; } + + +/* ============================================================================= + Tables + ========================================================================== */ + +table { border-collapse: collapse; border-spacing: 0; } +td { vertical-align: top; } + + +/* ==|== primary styles ===================================================== + Author: + ========================================================================== */ + +/* ==|== media queries ====================================================== + PLACEHOLDER Media Queries for Responsive Design. + These override the primary ('mobile first') styles + Modify as content requires. + ========================================================================== */ + +@media only screen and (min-width: 480px) { + /* Style adjustments for viewports 480px and over go here */ + +} + +@media only screen and (min-width: 768px) { + /* Style adjustments for viewports 768px and over go here */ + +} + + + +/* ==|== non-semantic helper classes ======================================== + Please define your styles before this section. + ========================================================================== */ + +/* For image replacement */ +.ir { display: block; border: 0; text-indent: -999em; overflow: hidden; background-color: transparent; background-repeat: no-repeat; text-align: left; direction: ltr; } +.ir br { display: none; } + +/* Hide from both screenreaders and browsers: h5bp.com/u */ +.hidden { display: none !important; visibility: hidden; } + +/* Hide only visually, but have it available for screenreaders: h5bp.com/v */ +.visuallyhidden { border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; } + +/* Extends the .visuallyhidden class to allow the element to be focusable when navigated to via the keyboard: h5bp.com/p */ +.visuallyhidden.focusable:active, .visuallyhidden.focusable:focus { clip: auto; height: auto; margin: 0; overflow: visible; position: static; width: auto; } + +/* Hide visually and from screenreaders, but maintain layout */ +.invisible { visibility: hidden; } + +/* Contain floats: h5bp.com/q */ +.clearfix:before, .clearfix:after { content: ""; display: table; } +.clearfix:after { clear: both; } +.clearfix { *zoom: 1; } + + + +/* ==|== print styles ======================================================= + Print styles. + Inlined to avoid required HTTP connection: h5bp.com/r + ========================================================================== */ + +@media print { + * { background: transparent !important; color: black !important; box-shadow:none !important; text-shadow: none !important; filter:none !important; -ms-filter: none !important; } /* Black prints faster: h5bp.com/s */ + a, a:visited { text-decoration: underline; } + a[href]:after { content: " (" attr(href) ")"; } + abbr[title]:after { content: " (" attr(title) ")"; } + .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } /* Don't show links for images, or javascript/internal links */ + pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } + table { display: table-header-group; } /* h5bp.com/t */ + tr, img { page-break-inside: avoid; } + img { max-width: 100% !important; } + @page { margin: 0.5cm; } + p, h2, h3 { orphans: 3; widows: 3; } + h2, h3 { page-break-after: avoid; } +} + +/* reflow reset for -webkit-margin-before: 1em */ +p { margin: 0; } + +html { + overflow-y: auto; + background-color: transparent; + height: 100%; +} + +body { + background: #fff; + font: normal 100%; + position: relative; + height: 100%; +} + +body, div, img, p, button, input, select, textarea { + box-sizing: border-box; +} + +.image { + display: block; +} + +input { + cursor: default; + display: block; +} + +input[type=button] { + background-color: #e5e9e8; + border: 1px solid #9daca9; + border-radius: 4px; + box-shadow: inset 0 1px #fff; + font: inherit; + letter-spacing: inherit; + text-indent: inherit; + color: inherit; +} + +input[type=button]:hover { + background-color: #eff1f1; +} + +input[type=button]:active { + background-color: #d2d6d6; + border: 1px solid #9daca9; + box-shadow: inset 0 1px rgba(0,0,0,0.1); +} + +/* Reset anchor styles to an unstyled default to be in parity with design surface. It + is presumed that most link styles in real-world designs are custom (non-default). */ +a, a:visited, a:hover, a:active { + color: inherit; + text-decoration: inherit; +} \ No newline at end of file diff --git a/openpype/hosts/aftereffects/api/extension/css/styles.css b/openpype/hosts/aftereffects/api/extension/css/styles.css new file mode 100644 index 00000000000..c9cf2b93acf --- /dev/null +++ b/openpype/hosts/aftereffects/api/extension/css/styles.css @@ -0,0 +1,51 @@ +/*Your styles*/ + + body { + margin: 10px; +} + + +#content { + margin-right:auto; + margin-left:auto; + vertical-align:middle; + width:100%; +} + + +#btn_test{ + width: 100%; +} + + + + +/* +Those classes will be edited at runtime with values specified +by the settings of the CC application +*/ +.hostFontColor{} +.hostFontFamily{} +.hostFontSize{} + +/*font family, color and size*/ +.hostFont{} +/*background color*/ +.hostBgd{} +/*lighter background color*/ +.hostBgdLight{} +/*darker background color*/ +.hostBgdDark{} +/*background color and font*/ +.hostElt{} + + +.hostButton{ + border:1px solid; + border-radius:2px; + height:20px; + vertical-align:bottom; + font-family:inherit; + color:inherit; + font-size:inherit; +} \ No newline at end of file diff --git a/openpype/hosts/aftereffects/api/extension/css/topcoat-desktop-dark.min.css b/openpype/hosts/aftereffects/api/extension/css/topcoat-desktop-dark.min.css new file mode 100644 index 00000000000..6b479def439 --- /dev/null +++ b/openpype/hosts/aftereffects/api/extension/css/topcoat-desktop-dark.min.css @@ -0,0 +1 @@ +.button-bar{display:table;table-layout:fixed;white-space:nowrap;margin:0;padding:0}.button-bar__item{display:table-cell;width:auto;border-radius:0}.button-bar__item>input{position:absolute;overflow:hidden;padding:0;border:0;opacity:.001;z-index:1;vertical-align:top;outline:0}.button-bar__button{border-radius:inherit}.button-bar__item:disabled{opacity:.3;cursor:default;pointer-events:none}.button,.topcoat-button,.topcoat-button--quiet,.topcoat-button--large,.topcoat-button--large--quiet,.topcoat-button--cta,.topcoat-button--large--cta,.topcoat-button-bar__button,.topcoat-button-bar__button--large{position:relative;display:inline-block;vertical-align:top;-moz-box-sizing:border-box;box-sizing:border-box;background-clip:padding-box;padding:0;margin:0;font:inherit;color:inherit;background:transparent;border:0;cursor:default;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;text-decoration:none}.button--quiet{background:transparent;border:1px solid transparent;box-shadow:none}.button--disabled,.topcoat-button:disabled,.topcoat-button--quiet:disabled,.topcoat-button--large:disabled,.topcoat-button--large--quiet:disabled,.topcoat-button--cta:disabled,.topcoat-button--large--cta:disabled,.topcoat-button-bar__button:disabled,.topcoat-button-bar__button--large:disabled{opacity:.3;cursor:default;pointer-events:none}.topcoat-button,.topcoat-button--quiet,.topcoat-button--large,.topcoat-button--large--quiet,.topcoat-button--cta,.topcoat-button--large--cta,.topcoat-button-bar__button,.topcoat-button-bar__button--large{padding:0 .563rem;font-size:12px;line-height:1.313rem;letter-spacing:0;color:#c6c8c8;text-shadow:0 -1px rgba(0,0,0,.69);vertical-align:top;background-color:#595b5b;box-shadow:inset 0 1px #737373;border:1px solid #333434;border-radius:4px}.topcoat-button:hover,.topcoat-button--quiet:hover,.topcoat-button--large:hover,.topcoat-button--large--quiet:hover,.topcoat-button-bar__button:hover,.topcoat-button-bar__button--large:hover{background-color:#626465}.topcoat-button:focus,.topcoat-button--quiet:focus,.topcoat-button--quiet:hover:focus,.topcoat-button--large:focus,.topcoat-button--large--quiet:focus,.topcoat-button--large--quiet:hover:focus,.topcoat-button--cta:focus,.topcoat-button--large--cta:focus,.topcoat-button-bar__button:focus,.topcoat-button-bar__button--large:focus{border:1px solid #0036ff;box-shadow:inset 0 1px rgba(255,255,255,.36),0 0 0 2px #6fb5f1;outline:0}.topcoat-button:active,.topcoat-button--large:active,.topcoat-button-bar__button:active,.topcoat-button-bar__button--large:active,:checked+.topcoat-button-bar__button{border:1px solid #333434;background-color:#3f4041;box-shadow:inset 0 1px rgba(0,0,0,.05)}.topcoat-button--quiet{background:transparent;border:1px solid transparent;box-shadow:none}.topcoat-button--quiet:hover,.topcoat-button--large--quiet:hover{text-shadow:0 -1px rgba(0,0,0,.69);border:1px solid #333434;box-shadow:inset 0 1px #737373}.topcoat-button--quiet:active,.topcoat-button--quiet:focus:active,.topcoat-button--large--quiet:active,.topcoat-button--large--quiet:focus:active{color:#c6c8c8;text-shadow:0 -1px rgba(0,0,0,.69);background-color:#3f4041;border:1px solid #333434;box-shadow:inset 0 1px rgba(0,0,0,.05)}.topcoat-button--large,.topcoat-button--large--quiet,.topcoat-button-bar__button--large{font-size:.875rem;font-weight:600;line-height:1.688rem;padding:0 .875rem}.topcoat-button--large--quiet{background:transparent;border:1px solid transparent;box-shadow:none}.topcoat-button--cta,.topcoat-button--large--cta{border:1px solid #134f7f;background-color:#288edf;box-shadow:inset 0 1px rgba(255,255,255,.36);color:#fff;font-weight:500;text-shadow:0 -1px rgba(0,0,0,.36)}.topcoat-button--cta:hover,.topcoat-button--large--cta:hover{background-color:#4ca1e4}.topcoat-button--cta:active,.topcoat-button--large--cta:active{background-color:#1e7dc8;box-shadow:inset 0 1px rgba(0,0,0,.12)}.topcoat-button--large--cta{font-size:.875rem;font-weight:600;line-height:1.688rem;padding:0 .875rem}.button-bar,.topcoat-button-bar{display:table;table-layout:fixed;white-space:nowrap;margin:0;padding:0}.button-bar__item,.topcoat-button-bar__item{display:table-cell;width:auto;border-radius:0}.button-bar__item>input,.topcoat-button-bar__item>input{position:absolute;overflow:hidden;padding:0;border:0;opacity:.001;z-index:1;vertical-align:top;outline:0}.button-bar__button{border-radius:inherit}.button-bar__item:disabled{opacity:.3;cursor:default;pointer-events:none}.topcoat-button-bar>.topcoat-button-bar__item:first-child{border-top-left-radius:4px;border-bottom-left-radius:4px}.topcoat-button-bar>.topcoat-button-bar__item:last-child{border-top-right-radius:4px;border-bottom-right-radius:4px}.topcoat-button-bar__item:first-child>.topcoat-button-bar__button,.topcoat-button-bar__item:first-child>.topcoat-button-bar__button--large{border-right:0}.topcoat-button-bar__item:last-child>.topcoat-button-bar__button,.topcoat-button-bar__item:last-child>.topcoat-button-bar__button--large{border-left:0}.topcoat-button-bar__button{border-radius:inherit}.topcoat-button-bar__button:focus,.topcoat-button-bar__button--large:focus{z-index:1}.topcoat-button-bar__button--large{border-radius:inherit}.button{position:relative;display:inline-block;vertical-align:top;-moz-box-sizing:border-box;box-sizing:border-box;background-clip:padding-box;padding:0;margin:0;font:inherit;color:inherit;background:transparent;border:0;cursor:default;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;text-decoration:none}.button--quiet{background:transparent;border:1px solid transparent;box-shadow:none}.button--disabled{opacity:.3;cursor:default;pointer-events:none}.button,.topcoat-button,.topcoat-button--quiet,.topcoat-button--large,.topcoat-button--large--quiet,.topcoat-button--cta,.topcoat-button--large--cta{position:relative;display:inline-block;vertical-align:top;-moz-box-sizing:border-box;box-sizing:border-box;background-clip:padding-box;padding:0;margin:0;font:inherit;color:inherit;background:transparent;border:0;cursor:default;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;text-decoration:none}.button--quiet{background:transparent;border:1px solid transparent;box-shadow:none}.button--disabled,.topcoat-button:disabled,.topcoat-button--quiet:disabled,.topcoat-button--large:disabled,.topcoat-button--large--quiet:disabled,.topcoat-button--cta:disabled,.topcoat-button--large--cta:disabled{opacity:.3;cursor:default;pointer-events:none}.topcoat-button,.topcoat-button--quiet,.topcoat-button--large,.topcoat-button--large--quiet,.topcoat-button--cta,.topcoat-button--large--cta{padding:0 .563rem;font-size:12px;line-height:1.313rem;letter-spacing:0;color:#c6c8c8;text-shadow:0 -1px rgba(0,0,0,.69);vertical-align:top;background-color:#595b5b;box-shadow:inset 0 1px #737373;border:1px solid #333434;border-radius:4px}.topcoat-button:hover,.topcoat-button--quiet:hover,.topcoat-button--large:hover,.topcoat-button--large--quiet:hover{background-color:#626465}.topcoat-button:focus,.topcoat-button--quiet:focus,.topcoat-button--quiet:hover:focus,.topcoat-button--large:focus,.topcoat-button--large--quiet:focus,.topcoat-button--large--quiet:hover:focus,.topcoat-button--cta:focus,.topcoat-button--large--cta:focus{border:1px solid #0036ff;box-shadow:inset 0 1px rgba(255,255,255,.36),0 0 0 2px #6fb5f1;outline:0}.topcoat-button:active,.topcoat-button--large:active{border:1px solid #333434;background-color:#3f4041;box-shadow:inset 0 1px rgba(0,0,0,.05)}.topcoat-button--quiet{background:transparent;border:1px solid transparent;box-shadow:none}.topcoat-button--quiet:hover,.topcoat-button--large--quiet:hover{text-shadow:0 -1px rgba(0,0,0,.69);border:1px solid #333434;box-shadow:inset 0 1px #737373}.topcoat-button--quiet:active,.topcoat-button--quiet:focus:active,.topcoat-button--large--quiet:active,.topcoat-button--large--quiet:focus:active{color:#c6c8c8;text-shadow:0 -1px rgba(0,0,0,.69);background-color:#3f4041;border:1px solid #333434;box-shadow:inset 0 1px rgba(0,0,0,.05)}.topcoat-button--large,.topcoat-button--large--quiet{font-size:.875rem;font-weight:600;line-height:1.688rem;padding:0 .875rem}.topcoat-button--large--quiet{background:transparent;border:1px solid transparent;box-shadow:none}.topcoat-button--cta,.topcoat-button--large--cta{border:1px solid #134f7f;background-color:#288edf;box-shadow:inset 0 1px rgba(255,255,255,.36);color:#fff;font-weight:500;text-shadow:0 -1px rgba(0,0,0,.36)}.topcoat-button--cta:hover,.topcoat-button--large--cta:hover{background-color:#4ca1e4}.topcoat-button--cta:active,.topcoat-button--large--cta:active{background-color:#1e7dc8;box-shadow:inset 0 1px rgba(0,0,0,.12)}.topcoat-button--large--cta{font-size:.875rem;font-weight:600;line-height:1.688rem;padding:0 .875rem}input[type=checkbox]{position:absolute;overflow:hidden;padding:0;border:0;opacity:.001;z-index:1;vertical-align:top;outline:0}.checkbox{-moz-box-sizing:border-box;box-sizing:border-box;background-clip:padding-box;position:relative;display:inline-block;vertical-align:top;cursor:default;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.checkbox__label{position:relative;display:inline-block;vertical-align:top;cursor:default;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.checkbox--disabled{opacity:.3;cursor:default;pointer-events:none}.checkbox:before,.checkbox:after{content:'';position:absolute}.checkbox:before{-moz-box-sizing:border-box;box-sizing:border-box;background-clip:padding-box}input[type=checkbox]{position:absolute;overflow:hidden;padding:0;border:0;opacity:.001;z-index:1;vertical-align:top;outline:0}.checkbox,.topcoat-checkbox__checkmark{-moz-box-sizing:border-box;box-sizing:border-box;background-clip:padding-box;position:relative;display:inline-block;vertical-align:top;cursor:default;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.checkbox__label,.topcoat-checkbox{position:relative;display:inline-block;vertical-align:top;cursor:default;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.checkbox--disabled,input[type=checkbox]:disabled+.topcoat-checkbox__checkmark{opacity:.3;cursor:default;pointer-events:none}.checkbox:before,.checkbox:after,.topcoat-checkbox__checkmark:before,.topcoat-checkbox__checkmark:after{content:'';position:absolute}.checkbox:before,.topcoat-checkbox__checkmark:before{-moz-box-sizing:border-box;box-sizing:border-box;background-clip:padding-box}.topcoat-checkbox__checkmark{height:1rem}input[type=checkbox]{height:1rem;width:1rem;margin-top:0;margin-right:-1rem;margin-bottom:-1rem;margin-left:0}input[type=checkbox]:checked+.topcoat-checkbox__checkmark:after{opacity:1}.topcoat-checkbox{line-height:1rem}.topcoat-checkbox__checkmark:before{width:1rem;height:1rem;background:#595b5b;border:1px solid #333434;border-radius:3px;box-shadow:inset 0 1px #737373}.topcoat-checkbox__checkmark{width:1rem;height:1rem}.topcoat-checkbox__checkmark:after{top:2px;left:1px;opacity:0;width:14px;height:4px;background:transparent;border:7px solid #c6c8c8;border-width:3px;border-top:0;border-right:0;border-radius:1px;-webkit-transform:rotate(-50deg);-ms-transform:rotate(-50deg);transform:rotate(-50deg)}input[type=checkbox]:focus+.topcoat-checkbox__checkmark:before{border:1px solid #0036ff;box-shadow:inset 0 1px rgba(255,255,255,.36),0 0 0 2px #6fb5f1}input[type=checkbox]:active+.topcoat-checkbox__checkmark:before{border:1px solid #333434;background-color:#3f4041;box-shadow:inset 0 1px rgba(0,0,0,.05)}input[type=checkbox]:disabled:active+.topcoat-checkbox__checkmark:before{border:1px solid #333434;background:#595b5b;box-shadow:inset 0 1px #737373}.button,.topcoat-icon-button,.topcoat-icon-button--quiet,.topcoat-icon-button--large,.topcoat-icon-button--large--quiet{position:relative;display:inline-block;vertical-align:top;-moz-box-sizing:border-box;box-sizing:border-box;background-clip:padding-box;padding:0;margin:0;font:inherit;color:inherit;background:transparent;border:0;cursor:default;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;text-decoration:none}.button--quiet{background:transparent;border:1px solid transparent;box-shadow:none}.button--disabled,.topcoat-icon-button:disabled,.topcoat-icon-button--quiet:disabled,.topcoat-icon-button--large:disabled,.topcoat-icon-button--large--quiet:disabled{opacity:.3;cursor:default;pointer-events:none}.topcoat-icon-button,.topcoat-icon-button--quiet,.topcoat-icon-button--large,.topcoat-icon-button--large--quiet{padding:0 .25rem;line-height:1.313rem;letter-spacing:0;color:#c6c8c8;text-shadow:0 -1px rgba(0,0,0,.69);vertical-align:baseline;background-color:#595b5b;box-shadow:inset 0 1px #737373;border:1px solid #333434;border-radius:4px}.topcoat-icon-button:hover,.topcoat-icon-button--quiet:hover,.topcoat-icon-button--large:hover,.topcoat-icon-button--large--quiet:hover{background-color:#626465}.topcoat-icon-button:focus,.topcoat-icon-button--quiet:focus,.topcoat-icon-button--quiet:hover:focus,.topcoat-icon-button--large:focus,.topcoat-icon-button--large--quiet:focus,.topcoat-icon-button--large--quiet:hover:focus{border:1px solid #0036ff;box-shadow:inset 0 1px rgba(255,255,255,.36),0 0 0 2px #6fb5f1;outline:0}.topcoat-icon-button:active,.topcoat-icon-button--large:active{border:1px solid #333434;background-color:#3f4041;box-shadow:inset 0 1px rgba(0,0,0,.05)}.topcoat-icon-button--quiet{background:transparent;border:1px solid transparent;box-shadow:none}.topcoat-icon-button--quiet:hover,.topcoat-icon-button--large--quiet:hover{text-shadow:0 -1px rgba(0,0,0,.69);border:1px solid #333434;box-shadow:inset 0 1px #737373}.topcoat-icon-button--quiet:active,.topcoat-icon-button--quiet:focus:active,.topcoat-icon-button--large--quiet:active,.topcoat-icon-button--large--quiet:focus:active{color:#c6c8c8;text-shadow:0 -1px rgba(0,0,0,.69);background-color:#3f4041;border:1px solid #333434;box-shadow:inset 0 1px rgba(0,0,0,.05)}.topcoat-icon-button--large,.topcoat-icon-button--large--quiet{width:1.688rem;height:1.688rem;line-height:1.688rem}.topcoat-icon-button--large--quiet{background:transparent;border:1px solid transparent;box-shadow:none}.topcoat-icon,.topcoat-icon--large{position:relative;display:inline-block;vertical-align:top;overflow:hidden;width:.81406rem;height:.81406rem;vertical-align:middle;top:-1px}.topcoat-icon--large{width:1.06344rem;height:1.06344rem;top:-2px}.input{padding:0;margin:0;font:inherit;color:inherit;background:transparent;border:0;-moz-box-sizing:border-box;box-sizing:border-box;background-clip:padding-box;vertical-align:top;outline:0}.input:disabled{opacity:.3;cursor:default;pointer-events:none}.list{padding:0;margin:0;font:inherit;color:inherit;background:transparent;border:0;cursor:default;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;overflow:auto;-webkit-overflow-scrolling:touch}.list__header{margin:0}.list__container{padding:0;margin:0;list-style-type:none}.list__item{margin:0;padding:0}.navigation-bar{-moz-box-sizing:border-box;box-sizing:border-box;background-clip:padding-box;white-space:nowrap;overflow:hidden;word-spacing:0;padding:0;margin:0;font:inherit;color:inherit;background:transparent;border:0;cursor:default;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.navigation-bar__item{-moz-box-sizing:border-box;box-sizing:border-box;background-clip:padding-box;position:relative;display:inline-block;vertical-align:top;padding:0;margin:0;font:inherit;color:inherit;background:transparent;border:0}.navigation-bar__title{padding:0;margin:0;font:inherit;color:inherit;background:transparent;border:0;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.notification{position:relative;display:inline-block;vertical-align:top;-moz-box-sizing:border-box;box-sizing:border-box;background-clip:padding-box;padding:0;margin:0;font:inherit;color:inherit;background:transparent;border:0;cursor:default;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;text-decoration:none}.notification,.topcoat-notification{position:relative;display:inline-block;vertical-align:top;-moz-box-sizing:border-box;box-sizing:border-box;background-clip:padding-box;padding:0;margin:0;font:inherit;color:inherit;background:transparent;border:0;cursor:default;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;text-decoration:none}.topcoat-notification{padding:.15em .5em .2em;border-radius:2px;background-color:#ec514e;color:#fff}input[type=radio]{position:absolute;overflow:hidden;padding:0;border:0;opacity:.001;z-index:1;vertical-align:top;outline:0}.radio-button{-moz-box-sizing:border-box;box-sizing:border-box;background-clip:padding-box;position:relative;display:inline-block;vertical-align:top;cursor:default;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.radio-button__label{position:relative;display:inline-block;vertical-align:top;cursor:default;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.radio-button:before,.radio-button:after{content:'';position:absolute;border-radius:100%}.radio-button:after{top:50%;left:50%;-webkit-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.radio-button:before{-moz-box-sizing:border-box;box-sizing:border-box;background-clip:padding-box}.radio-button--disabled{opacity:.3;cursor:default;pointer-events:none}input[type=radio]{position:absolute;overflow:hidden;padding:0;border:0;opacity:.001;z-index:1;vertical-align:top;outline:0}.radio-button,.topcoat-radio-button__checkmark{-moz-box-sizing:border-box;box-sizing:border-box;background-clip:padding-box;position:relative;display:inline-block;vertical-align:top;cursor:default;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.radio-button__label,.topcoat-radio-button{position:relative;display:inline-block;vertical-align:top;cursor:default;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.radio-button:before,.radio-button:after,.topcoat-radio-button__checkmark:before,.topcoat-radio-button__checkmark:after{content:'';position:absolute;border-radius:100%}.radio-button:after,.topcoat-radio-button__checkmark:after{top:50%;left:50%;-webkit-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.radio-button:before,.topcoat-radio-button__checkmark:before{-moz-box-sizing:border-box;box-sizing:border-box;background-clip:padding-box}.radio-button--disabled,input[type=radio]:disabled+.topcoat-radio-button__checkmark{opacity:.3;cursor:default;pointer-events:none}input[type=radio]{height:1.063rem;width:1.063rem;margin-top:0;margin-right:-1.063rem;margin-bottom:-1.063rem;margin-left:0}input[type=radio]:checked+.topcoat-radio-button__checkmark:after{opacity:1}.topcoat-radio-button{color:#c6c8c8;line-height:1.063rem}.topcoat-radio-button__checkmark:before{width:1.063rem;height:1.063rem;background:#595b5b;border:1px solid #333434;box-shadow:inset 0 1px #737373}.topcoat-radio-button__checkmark{position:relative;width:1.063rem;height:1.063rem}.topcoat-radio-button__checkmark:after{opacity:0;width:.313rem;height:.313rem;background:#c6c8c8;border:1px solid rgba(0,0,0,.05);box-shadow:0 1px rgba(255,255,255,.1);-webkit-transform:none;-ms-transform:none;transform:none;top:.313rem;left:.313rem}input[type=radio]:focus+.topcoat-radio-button__checkmark:before{border:1px solid #0036ff;box-shadow:inset 0 1px rgba(255,255,255,.36),0 0 0 2px #6fb5f1}input[type=radio]:active+.topcoat-radio-button__checkmark:before{border:1px solid #333434;background-color:#3f4041;box-shadow:inset 0 1px rgba(0,0,0,.05)}input[type=radio]:disabled:active+.topcoat-radio-button__checkmark:before{border:1px solid #333434;background:#595b5b;box-shadow:inset 0 1px #737373}.range{padding:0;margin:0;font:inherit;color:inherit;background:transparent;border:0;-moz-box-sizing:border-box;box-sizing:border-box;background-clip:padding-box;vertical-align:top;outline:0;-webkit-appearance:none}.range__thumb{cursor:pointer}.range__thumb--webkit{cursor:pointer;-webkit-appearance:none}.range:disabled{opacity:.3;cursor:default;pointer-events:none}.range,.topcoat-range{padding:0;margin:0;font:inherit;color:inherit;background:transparent;border:0;-moz-box-sizing:border-box;box-sizing:border-box;background-clip:padding-box;vertical-align:top;outline:0;-webkit-appearance:none}.range__thumb,.topcoat-range::-moz-range-thumb{cursor:pointer}.range__thumb--webkit,.topcoat-range::-webkit-slider-thumb{cursor:pointer;-webkit-appearance:none}.range:disabled,.topcoat-range:disabled{opacity:.3;cursor:default;pointer-events:none}.topcoat-range{border-radius:4px;border:1px solid #333434;background-color:#454646;height:.5rem;border-radius:15px}.topcoat-range::-moz-range-track{border-radius:4px;border:1px solid #333434;background-color:#454646;height:.5rem;border-radius:15px}.topcoat-range::-webkit-slider-thumb{height:1.313rem;width:.75rem;background-color:#595b5b;border:1px solid #333434;border-radius:4px;box-shadow:inset 0 1px #737373}.topcoat-range::-moz-range-thumb{height:1.313rem;width:.75rem;background-color:#595b5b;border:1px solid #333434;border-radius:4px;box-shadow:inset 0 1px #737373}.topcoat-range:focus::-webkit-slider-thumb{border:1px solid #0036ff;box-shadow:inset 0 1px rgba(255,255,255,.36),0 0 0 2px #6fb5f1}.topcoat-range:focus::-moz-range-thumb{border:1px solid #0036ff;box-shadow:inset 0 1px rgba(255,255,255,.36),0 0 0 2px #6fb5f1}.topcoat-range:active::-webkit-slider-thumb{border:1px solid #333434;box-shadow:inset 0 1px #737373}.topcoat-range:active::-moz-range-thumb{border:1px solid #333434;box-shadow:inset 0 1px #737373}.search-input{padding:0;margin:0;font:inherit;color:inherit;background:transparent;border:0;-moz-box-sizing:border-box;box-sizing:border-box;background-clip:padding-box;vertical-align:top;outline:0;-webkit-appearance:none}input[type=search]::-webkit-search-cancel-button{-webkit-appearance:none}.search-input:disabled{opacity:.3;cursor:default;pointer-events:none}.search-input,.topcoat-search-input,.topcoat-search-input--large{padding:0;margin:0;font:inherit;color:inherit;background:transparent;border:0;-moz-box-sizing:border-box;box-sizing:border-box;background-clip:padding-box;vertical-align:top;outline:0;-webkit-appearance:none}input[type=search]::-webkit-search-cancel-button{-webkit-appearance:none}.search-input:disabled,.topcoat-search-input:disabled,.topcoat-search-input--large:disabled{opacity:.3;cursor:default;pointer-events:none}.topcoat-search-input,.topcoat-search-input--large{line-height:1.313rem;height:1.313rem;font-size:12px;border:1px solid #333434;background-color:#454646;box-shadow:inset 0 1px 0 rgba(0,0,0,.23);color:#c6c8c8;padding:0 0 0 1.3rem;border-radius:15px;background-image:url(../img/search.svg);background-position:1rem center;background-repeat:no-repeat;background-size:12px}.topcoat-search-input:focus,.topcoat-search-input--large:focus{background-color:#595b5b;color:#fff;border:1px solid #0036ff;box-shadow:inset 0 1px 0 rgba(0,0,0,.23),0 0 0 2px #6fb5f1}.topcoat-search-input::-webkit-search-cancel-button,.topcoat-search-input::-webkit-search-decoration,.topcoat-search-input--large::-webkit-search-cancel-button,.topcoat-search-input--large::-webkit-search-decoration{margin-right:5px}.topcoat-search-input:focus::-webkit-input-placeholder,.topcoat-search-input:focus::-webkit-input-placeholder{color:#c6c8c8}.topcoat-search-input:disabled::-webkit-input-placeholder{color:#fff}.topcoat-search-input:disabled::-moz-placeholder{color:#fff}.topcoat-search-input:disabled:-ms-input-placeholder{color:#fff}.topcoat-search-input--large{line-height:1.688rem;height:1.688rem;font-size:.875rem;font-weight:400;padding:0 0 0 1.8rem;border-radius:25px;background-position:1.2rem center;background-size:.875rem}.topcoat-search-input--large:disabled{color:#fff}.topcoat-search-input--large:disabled::-webkit-input-placeholder{color:#fff}.topcoat-search-input--large:disabled::-moz-placeholder{color:#fff}.topcoat-search-input--large:disabled:-ms-input-placeholder{color:#fff}.switch{position:relative;display:inline-block;vertical-align:top;-moz-box-sizing:border-box;box-sizing:border-box;background-clip:padding-box}.switch__input{position:absolute;overflow:hidden;padding:0;border:0;opacity:.001;z-index:1;vertical-align:top;outline:0}.switch__toggle{position:relative;display:inline-block;vertical-align:top;-moz-box-sizing:border-box;box-sizing:border-box;background-clip:padding-box;padding:0;margin:0;font:inherit;color:inherit;background:transparent;border:0;cursor:default;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.switch__toggle:before,.switch__toggle:after{content:'';position:absolute;z-index:-1;-moz-box-sizing:border-box;box-sizing:border-box;background-clip:padding-box}.switch--disabled{opacity:.3;cursor:default;pointer-events:none}.switch,.topcoat-switch{position:relative;display:inline-block;vertical-align:top;-moz-box-sizing:border-box;box-sizing:border-box;background-clip:padding-box}.switch__input,.topcoat-switch__input{position:absolute;overflow:hidden;padding:0;border:0;opacity:.001;z-index:1;vertical-align:top;outline:0}.switch__toggle,.topcoat-switch__toggle{position:relative;display:inline-block;vertical-align:top;-moz-box-sizing:border-box;box-sizing:border-box;background-clip:padding-box;padding:0;margin:0;font:inherit;color:inherit;background:transparent;border:0;cursor:default;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.switch__toggle:before,.switch__toggle:after,.topcoat-switch__toggle:before,.topcoat-switch__toggle:after{content:'';position:absolute;z-index:-1;-moz-box-sizing:border-box;box-sizing:border-box;background-clip:padding-box}.switch--disabled,.topcoat-switch__input:disabled+.topcoat-switch__toggle{opacity:.3;cursor:default;pointer-events:none}.topcoat-switch{font-size:12px;padding:0 .563rem;border-radius:4px;border:1px solid #333434;overflow:hidden;width:3.5rem}.topcoat-switch__toggle:before,.topcoat-switch__toggle:after{top:-1px;width:2.6rem}.topcoat-switch__toggle:before{content:'ON';color:#288edf;background-color:#3f4041;right:.8rem;padding-left:.75rem}.topcoat-switch__toggle{line-height:1.313rem;height:1.313rem;width:1rem;border-radius:4px;color:#c6c8c8;text-shadow:0 -1px rgba(0,0,0,.69);background-color:#595b5b;border:1px solid #333434;margin-left:-.6rem;margin-bottom:-1px;margin-top:-1px;box-shadow:inset 0 1px #737373;-webkit-transition:margin-left .05s ease-in-out;transition:margin-left .05s ease-in-out}.topcoat-switch__toggle:after{content:'OFF';background-color:#3f4041;left:.8rem;padding-left:.6rem}.topcoat-switch__input:checked+.topcoat-switch__toggle{margin-left:1.85rem}.topcoat-switch__input:active+.topcoat-switch__toggle{border:1px solid #333434;box-shadow:inset 0 1px #737373}.topcoat-switch__input:focus+.topcoat-switch__toggle{border:1px solid #0036ff;box-shadow:0 0 0 2px #6fb5f1}.topcoat-switch__input:disabled+.topcoat-switch__toggle:after,.topcoat-switch__input:disabled+.topcoat-switch__toggle:before{background:transparent}.button,.topcoat-tab-bar__button{position:relative;display:inline-block;vertical-align:top;-moz-box-sizing:border-box;box-sizing:border-box;background-clip:padding-box;padding:0;margin:0;font:inherit;color:inherit;background:transparent;border:0;cursor:default;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;text-decoration:none}.button--quiet{background:transparent;border:1px solid transparent;box-shadow:none}.button--disabled,.topcoat-tab-bar__button:disabled{opacity:.3;cursor:default;pointer-events:none}.button-bar,.topcoat-tab-bar{display:table;table-layout:fixed;white-space:nowrap;margin:0;padding:0}.button-bar__item,.topcoat-tab-bar__item{display:table-cell;width:auto;border-radius:0}.button-bar__item>input,.topcoat-tab-bar__item>input{position:absolute;overflow:hidden;padding:0;border:0;opacity:.001;z-index:1;vertical-align:top;outline:0}.button-bar__button{border-radius:inherit}.button-bar__item:disabled{opacity:.3;cursor:default;pointer-events:none}.topcoat-tab-bar__button{padding:0 .563rem;height:1.313rem;line-height:1.313rem;letter-spacing:0;color:#c6c8c8;text-shadow:0 -1px rgba(0,0,0,.69);vertical-align:top;background-color:#595b5b;box-shadow:inset 0 1px #737373;border-top:1px solid #333434}.topcoat-tab-bar__button:active,.topcoat-tab-bar__button--large:active,:checked+.topcoat-tab-bar__button{color:#288edf;background-color:#3f4041;box-shadow:inset 0 0 1px rgba(0,0,0,.05)}.topcoat-tab-bar__button:focus,.topcoat-tab-bar__button--large:focus{z-index:1;box-shadow:inset 0 1px rgba(255,255,255,.36),0 0 0 2px #6fb5f1;outline:0}.input,.topcoat-text-input,.topcoat-text-input--large{padding:0;margin:0;font:inherit;color:inherit;background:transparent;border:0;-moz-box-sizing:border-box;box-sizing:border-box;background-clip:padding-box;vertical-align:top;outline:0}.input:disabled,.topcoat-text-input:disabled,.topcoat-text-input--large:disabled{opacity:.3;cursor:default;pointer-events:none}.topcoat-text-input,.topcoat-text-input--large{line-height:1.313rem;font-size:12px;letter-spacing:0;padding:0 .563rem;border:1px solid #333434;border-radius:4px;background-color:#454646;box-shadow:inset 0 1px rgba(0,0,0,.05);color:#c6c8c8;vertical-align:top}.topcoat-text-input:focus,.topcoat-text-input--large:focus{background-color:#595b5b;color:#fff;border:1px solid #0036ff;box-shadow:0 0 0 2px #6fb5f1}.topcoat-text-input:disabled::-webkit-input-placeholder{color:#fff}.topcoat-text-input:disabled::-moz-placeholder{color:#fff}.topcoat-text-input:disabled:-ms-input-placeholder{color:#fff}.topcoat-text-input:invalid{border:1px solid #ec514e}.topcoat-text-input--large{line-height:1.688rem;font-size:.875rem}.topcoat-text-input--large:disabled{color:#fff}.topcoat-text-input--large:disabled::-webkit-input-placeholder{color:#fff}.topcoat-text-input--large:disabled::-moz-placeholder{color:#fff}.topcoat-text-input--large:disabled:-ms-input-placeholder{color:#fff}.topcoat-text-input--large:invalid{border:1px solid #ec514e}.textarea{-moz-box-sizing:border-box;box-sizing:border-box;background-clip:padding-box;padding:0;margin:0;font:inherit;color:inherit;background:transparent;border:0;vertical-align:top;resize:none;outline:0}.textarea:disabled{opacity:.3;cursor:default;pointer-events:none}.textarea,.topcoat-textarea,.topcoat-textarea--large{-moz-box-sizing:border-box;box-sizing:border-box;background-clip:padding-box;padding:0;margin:0;font:inherit;color:inherit;background:transparent;border:0;vertical-align:top;resize:none;outline:0}.textarea:disabled,.topcoat-textarea:disabled,.topcoat-textarea--large:disabled{opacity:.3;cursor:default;pointer-events:none}.topcoat-textarea,.topcoat-textarea--large{padding:1rem;font-size:1rem;font-weight:400;border-radius:4px;line-height:1.313rem;border:1px solid #333434;background-color:#454646;box-shadow:inset 0 1px rgba(0,0,0,.05);color:#c6c8c8;letter-spacing:0}.topcoat-textarea:focus,.topcoat-textarea--large:focus{background-color:#595b5b;color:#fff;border:1px solid #0036ff;box-shadow:0 0 0 2px #6fb5f1}.topcoat-textarea:disabled::-webkit-input-placeholder{color:#fff}.topcoat-textarea:disabled::-moz-placeholder{color:#fff}.topcoat-textarea:disabled:-ms-input-placeholder{color:#fff}.topcoat-textarea--large{font-size:1.3rem;line-height:1.688rem}.topcoat-textarea--large:disabled{color:#fff}.topcoat-textarea--large:disabled::-webkit-input-placeholder{color:#fff}.topcoat-textarea--large:disabled::-moz-placeholder{color:#fff}.topcoat-textarea--large:disabled:-ms-input-placeholder{color:#fff}@font-face{font-family:"Source Sans";src:url(../font/SourceSansPro-Regular.otf)}@font-face{font-family:"Source Sans";src:url(../font/SourceSansPro-Light.otf);font-weight:200}@font-face{font-family:"Source Sans";src:url(../font/SourceSansPro-Semibold.otf);font-weight:600}body{margin:0;padding:0;background:#4b4d4e;color:#000;font:16px "Source Sans",helvetica,arial,sans-serif;font-weight:400}:focus{outline-color:transparent;outline-style:none}.topcoat-icon--menu-stack{background:url(../img/hamburger_light.svg) no-repeat;background-size:cover}.quarter{width:25%}.half{width:50%}.three-quarters{width:75%}.third{width:33.333%}.two-thirds{width:66.666%}.full{width:100%}.left{text-align:left}.center{text-align:center}.right{text-align:right}.reset-ui{-moz-box-sizing:border-box;box-sizing:border-box;background-clip:padding-box;position:relative;display:inline-block;vertical-align:top;padding:0;margin:0;font:inherit;color:inherit;background:transparent;border:0;cursor:default;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;text-overflow:ellipsis;white-space:nowrap;overflow:hidden} \ No newline at end of file diff --git a/openpype/hosts/aftereffects/api/extension/icons/iconDarkNormal.png b/openpype/hosts/aftereffects/api/extension/icons/iconDarkNormal.png new file mode 100644 index 0000000000000000000000000000000000000000..b8652a85b842b12d45e495c1d496590da0e26f01 GIT binary patch literal 18659 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 literal 0 HcmV?d00001 diff --git a/openpype/hosts/aftereffects/api/extension/icons/iconDarkRollover.png b/openpype/hosts/aftereffects/api/extension/icons/iconDarkRollover.png new file mode 100644 index 0000000000000000000000000000000000000000..49edd7ca278ed7d0047effd37242f2bed1e9be27 GIT binary patch literal 18663 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 literal 0 HcmV?d00001 diff --git a/openpype/hosts/aftereffects/api/extension/icons/iconDisabled.png b/openpype/hosts/aftereffects/api/extension/icons/iconDisabled.png new file mode 100644 index 0000000000000000000000000000000000000000..49edd7ca278ed7d0047effd37242f2bed1e9be27 GIT binary patch literal 18663 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 literal 0 HcmV?d00001 diff --git a/openpype/hosts/aftereffects/api/extension/icons/iconNormal.png b/openpype/hosts/aftereffects/api/extension/icons/iconNormal.png new file mode 100644 index 0000000000000000000000000000000000000000..199326f2eac3fee033a163b7e3fe395057746c4c GIT binary patch literal 18225 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 literal 0 HcmV?d00001 diff --git a/openpype/hosts/aftereffects/api/extension/icons/iconRollover.png b/openpype/hosts/aftereffects/api/extension/icons/iconRollover.png new file mode 100644 index 0000000000000000000000000000000000000000..ff62645798592fa733626e43741031a57d34c61a GIT binary patch literal 18664 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 literal 0 HcmV?d00001 diff --git a/openpype/hosts/aftereffects/api/extension/index.html b/openpype/hosts/aftereffects/api/extension/index.html new file mode 100644 index 00000000000..9e39bf1accc --- /dev/null +++ b/openpype/hosts/aftereffects/api/extension/index.html @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/openpype/hosts/aftereffects/api/extension/js/libs/CSInterface.js b/openpype/hosts/aftereffects/api/extension/js/libs/CSInterface.js new file mode 100644 index 00000000000..4239391efd0 --- /dev/null +++ b/openpype/hosts/aftereffects/api/extension/js/libs/CSInterface.js @@ -0,0 +1,1193 @@ +/************************************************************************************************** +* +* ADOBE SYSTEMS INCORPORATED +* Copyright 2013 Adobe Systems Incorporated +* All Rights Reserved. +* +* NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the +* terms of the Adobe license agreement accompanying it. If you have received this file from a +* source other than Adobe, then your use, modification, or distribution of it requires the prior +* written permission of Adobe. +* +**************************************************************************************************/ + +/** CSInterface - v8.0.0 */ + +/** + * Stores constants for the window types supported by the CSXS infrastructure. + */ +function CSXSWindowType() +{ +} + +/** Constant for the CSXS window type Panel. */ +CSXSWindowType._PANEL = "Panel"; + +/** Constant for the CSXS window type Modeless. */ +CSXSWindowType._MODELESS = "Modeless"; + +/** Constant for the CSXS window type ModalDialog. */ +CSXSWindowType._MODAL_DIALOG = "ModalDialog"; + +/** EvalScript error message */ +EvalScript_ErrMessage = "EvalScript error."; + +/** + * @class Version + * Defines a version number with major, minor, micro, and special + * components. The major, minor and micro values are numeric; the special + * value can be any string. + * + * @param major The major version component, a positive integer up to nine digits long. + * @param minor The minor version component, a positive integer up to nine digits long. + * @param micro The micro version component, a positive integer up to nine digits long. + * @param special The special version component, an arbitrary string. + * + * @return A new \c Version object. + */ +function Version(major, minor, micro, special) +{ + this.major = major; + this.minor = minor; + this.micro = micro; + this.special = special; +} + +/** + * The maximum value allowed for a numeric version component. + * This reflects the maximum value allowed in PlugPlug and the manifest schema. + */ +Version.MAX_NUM = 999999999; + +/** + * @class VersionBound + * Defines a boundary for a version range, which associates a \c Version object + * with a flag for whether it is an inclusive or exclusive boundary. + * + * @param version The \c #Version object. + * @param inclusive True if this boundary is inclusive, false if it is exclusive. + * + * @return A new \c VersionBound object. + */ +function VersionBound(version, inclusive) +{ + this.version = version; + this.inclusive = inclusive; +} + +/** + * @class VersionRange + * Defines a range of versions using a lower boundary and optional upper boundary. + * + * @param lowerBound The \c #VersionBound object. + * @param upperBound The \c #VersionBound object, or null for a range with no upper boundary. + * + * @return A new \c VersionRange object. + */ +function VersionRange(lowerBound, upperBound) +{ + this.lowerBound = lowerBound; + this.upperBound = upperBound; +} + +/** + * @class Runtime + * Represents a runtime related to the CEP infrastructure. + * Extensions can declare dependencies on particular + * CEP runtime versions in the extension manifest. + * + * @param name The runtime name. + * @param version A \c #VersionRange object that defines a range of valid versions. + * + * @return A new \c Runtime object. + */ +function Runtime(name, versionRange) +{ + this.name = name; + this.versionRange = versionRange; +} + +/** +* @class Extension +* Encapsulates a CEP-based extension to an Adobe application. +* +* @param id The unique identifier of this extension. +* @param name The localizable display name of this extension. +* @param mainPath The path of the "index.html" file. +* @param basePath The base path of this extension. +* @param windowType The window type of the main window of this extension. + Valid values are defined by \c #CSXSWindowType. +* @param width The default width in pixels of the main window of this extension. +* @param height The default height in pixels of the main window of this extension. +* @param minWidth The minimum width in pixels of the main window of this extension. +* @param minHeight The minimum height in pixels of the main window of this extension. +* @param maxWidth The maximum width in pixels of the main window of this extension. +* @param maxHeight The maximum height in pixels of the main window of this extension. +* @param defaultExtensionDataXml The extension data contained in the default \c ExtensionDispatchInfo section of the extension manifest. +* @param specialExtensionDataXml The extension data contained in the application-specific \c ExtensionDispatchInfo section of the extension manifest. +* @param requiredRuntimeList An array of \c Runtime objects for runtimes required by this extension. +* @param isAutoVisible True if this extension is visible on loading. +* @param isPluginExtension True if this extension has been deployed in the Plugins folder of the host application. +* +* @return A new \c Extension object. +*/ +function Extension(id, name, mainPath, basePath, windowType, width, height, minWidth, minHeight, maxWidth, maxHeight, + defaultExtensionDataXml, specialExtensionDataXml, requiredRuntimeList, isAutoVisible, isPluginExtension) +{ + this.id = id; + this.name = name; + this.mainPath = mainPath; + this.basePath = basePath; + this.windowType = windowType; + this.width = width; + this.height = height; + this.minWidth = minWidth; + this.minHeight = minHeight; + this.maxWidth = maxWidth; + this.maxHeight = maxHeight; + this.defaultExtensionDataXml = defaultExtensionDataXml; + this.specialExtensionDataXml = specialExtensionDataXml; + this.requiredRuntimeList = requiredRuntimeList; + this.isAutoVisible = isAutoVisible; + this.isPluginExtension = isPluginExtension; +} + +/** + * @class CSEvent + * A standard JavaScript event, the base class for CEP events. + * + * @param type The name of the event type. + * @param scope The scope of event, can be "GLOBAL" or "APPLICATION". + * @param appId The unique identifier of the application that generated the event. + * @param extensionId The unique identifier of the extension that generated the event. + * + * @return A new \c CSEvent object + */ +function CSEvent(type, scope, appId, extensionId) +{ + this.type = type; + this.scope = scope; + this.appId = appId; + this.extensionId = extensionId; +} + +/** Event-specific data. */ +CSEvent.prototype.data = ""; + +/** + * @class SystemPath + * Stores operating-system-specific location constants for use in the + * \c #CSInterface.getSystemPath() method. + * @return A new \c SystemPath object. + */ +function SystemPath() +{ +} + +/** The path to user data. */ +SystemPath.USER_DATA = "userData"; + +/** The path to common files for Adobe applications. */ +SystemPath.COMMON_FILES = "commonFiles"; + +/** The path to the user's default document folder. */ +SystemPath.MY_DOCUMENTS = "myDocuments"; + +/** @deprecated. Use \c #SystemPath.Extension. */ +SystemPath.APPLICATION = "application"; + +/** The path to current extension. */ +SystemPath.EXTENSION = "extension"; + +/** The path to hosting application's executable. */ +SystemPath.HOST_APPLICATION = "hostApplication"; + +/** + * @class ColorType + * Stores color-type constants. + */ +function ColorType() +{ +} + +/** RGB color type. */ +ColorType.RGB = "rgb"; + +/** Gradient color type. */ +ColorType.GRADIENT = "gradient"; + +/** Null color type. */ +ColorType.NONE = "none"; + +/** + * @class RGBColor + * Stores an RGB color with red, green, blue, and alpha values. + * All values are in the range [0.0 to 255.0]. Invalid numeric values are + * converted to numbers within this range. + * + * @param red The red value, in the range [0.0 to 255.0]. + * @param green The green value, in the range [0.0 to 255.0]. + * @param blue The blue value, in the range [0.0 to 255.0]. + * @param alpha The alpha (transparency) value, in the range [0.0 to 255.0]. + * The default, 255.0, means that the color is fully opaque. + * + * @return A new RGBColor object. + */ +function RGBColor(red, green, blue, alpha) +{ + this.red = red; + this.green = green; + this.blue = blue; + this.alpha = alpha; +} + +/** + * @class Direction + * A point value in which the y component is 0 and the x component + * is positive or negative for a right or left direction, + * or the x component is 0 and the y component is positive or negative for + * an up or down direction. + * + * @param x The horizontal component of the point. + * @param y The vertical component of the point. + * + * @return A new \c Direction object. + */ +function Direction(x, y) +{ + this.x = x; + this.y = y; +} + +/** + * @class GradientStop + * Stores gradient stop information. + * + * @param offset The offset of the gradient stop, in the range [0.0 to 1.0]. + * @param rgbColor The color of the gradient at this point, an \c #RGBColor object. + * + * @return GradientStop object. + */ +function GradientStop(offset, rgbColor) +{ + this.offset = offset; + this.rgbColor = rgbColor; +} + +/** + * @class GradientColor + * Stores gradient color information. + * + * @param type The gradient type, must be "linear". + * @param direction A \c #Direction object for the direction of the gradient + (up, down, right, or left). + * @param numStops The number of stops in the gradient. + * @param gradientStopList An array of \c #GradientStop objects. + * + * @return A new \c GradientColor object. + */ +function GradientColor(type, direction, numStops, arrGradientStop) +{ + this.type = type; + this.direction = direction; + this.numStops = numStops; + this.arrGradientStop = arrGradientStop; +} + +/** + * @class UIColor + * Stores color information, including the type, anti-alias level, and specific color + * values in a color object of an appropriate type. + * + * @param type The color type, 1 for "rgb" and 2 for "gradient". + The supplied color object must correspond to this type. + * @param antialiasLevel The anti-alias level constant. + * @param color A \c #RGBColor or \c #GradientColor object containing specific color information. + * + * @return A new \c UIColor object. + */ +function UIColor(type, antialiasLevel, color) +{ + this.type = type; + this.antialiasLevel = antialiasLevel; + this.color = color; +} + +/** + * @class AppSkinInfo + * Stores window-skin properties, such as color and font. All color parameter values are \c #UIColor objects except that systemHighlightColor is \c #RGBColor object. + * + * @param baseFontFamily The base font family of the application. + * @param baseFontSize The base font size of the application. + * @param appBarBackgroundColor The application bar background color. + * @param panelBackgroundColor The background color of the extension panel. + * @param appBarBackgroundColorSRGB The application bar background color, as sRGB. + * @param panelBackgroundColorSRGB The background color of the extension panel, as sRGB. + * @param systemHighlightColor The highlight color of the extension panel, if provided by the host application. Otherwise, the operating-system highlight color. + * + * @return AppSkinInfo object. + */ +function AppSkinInfo(baseFontFamily, baseFontSize, appBarBackgroundColor, panelBackgroundColor, appBarBackgroundColorSRGB, panelBackgroundColorSRGB, systemHighlightColor) +{ + this.baseFontFamily = baseFontFamily; + this.baseFontSize = baseFontSize; + this.appBarBackgroundColor = appBarBackgroundColor; + this.panelBackgroundColor = panelBackgroundColor; + this.appBarBackgroundColorSRGB = appBarBackgroundColorSRGB; + this.panelBackgroundColorSRGB = panelBackgroundColorSRGB; + this.systemHighlightColor = systemHighlightColor; +} + +/** + * @class HostEnvironment + * Stores information about the environment in which the extension is loaded. + * + * @param appName The application's name. + * @param appVersion The application's version. + * @param appLocale The application's current license locale. + * @param appUILocale The application's current UI locale. + * @param appId The application's unique identifier. + * @param isAppOnline True if the application is currently online. + * @param appSkinInfo An \c #AppSkinInfo object containing the application's default color and font styles. + * + * @return A new \c HostEnvironment object. + */ +function HostEnvironment(appName, appVersion, appLocale, appUILocale, appId, isAppOnline, appSkinInfo) +{ + this.appName = appName; + this.appVersion = appVersion; + this.appLocale = appLocale; + this.appUILocale = appUILocale; + this.appId = appId; + this.isAppOnline = isAppOnline; + this.appSkinInfo = appSkinInfo; +} + +/** + * @class HostCapabilities + * Stores information about the host capabilities. + * + * @param EXTENDED_PANEL_MENU True if the application supports panel menu. + * @param EXTENDED_PANEL_ICONS True if the application supports panel icon. + * @param DELEGATE_APE_ENGINE True if the application supports delegated APE engine. + * @param SUPPORT_HTML_EXTENSIONS True if the application supports HTML extensions. + * @param DISABLE_FLASH_EXTENSIONS True if the application disables FLASH extensions. + * + * @return A new \c HostCapabilities object. + */ +function HostCapabilities(EXTENDED_PANEL_MENU, EXTENDED_PANEL_ICONS, DELEGATE_APE_ENGINE, SUPPORT_HTML_EXTENSIONS, DISABLE_FLASH_EXTENSIONS) +{ + this.EXTENDED_PANEL_MENU = EXTENDED_PANEL_MENU; + this.EXTENDED_PANEL_ICONS = EXTENDED_PANEL_ICONS; + this.DELEGATE_APE_ENGINE = DELEGATE_APE_ENGINE; + this.SUPPORT_HTML_EXTENSIONS = SUPPORT_HTML_EXTENSIONS; + this.DISABLE_FLASH_EXTENSIONS = DISABLE_FLASH_EXTENSIONS; // Since 5.0.0 +} + +/** + * @class ApiVersion + * Stores current api version. + * + * Since 4.2.0 + * + * @param major The major version + * @param minor The minor version. + * @param micro The micro version. + * + * @return ApiVersion object. + */ +function ApiVersion(major, minor, micro) +{ + this.major = major; + this.minor = minor; + this.micro = micro; +} + +/** + * @class MenuItemStatus + * Stores flyout menu item status + * + * Since 5.2.0 + * + * @param menuItemLabel The menu item label. + * @param enabled True if user wants to enable the menu item. + * @param checked True if user wants to check the menu item. + * + * @return MenuItemStatus object. + */ +function MenuItemStatus(menuItemLabel, enabled, checked) +{ + this.menuItemLabel = menuItemLabel; + this.enabled = enabled; + this.checked = checked; +} + +/** + * @class ContextMenuItemStatus + * Stores the status of the context menu item. + * + * Since 5.2.0 + * + * @param menuItemID The menu item id. + * @param enabled True if user wants to enable the menu item. + * @param checked True if user wants to check the menu item. + * + * @return MenuItemStatus object. + */ +function ContextMenuItemStatus(menuItemID, enabled, checked) +{ + this.menuItemID = menuItemID; + this.enabled = enabled; + this.checked = checked; +} +//------------------------------ CSInterface ---------------------------------- + +/** + * @class CSInterface + * This is the entry point to the CEP extensibility infrastructure. + * Instantiate this object and use it to: + *
    + *
  • Access information about the host application in which an extension is running
  • + *
  • Launch an extension
  • + *
  • Register interest in event notifications, and dispatch events
  • + *
+ * + * @return A new \c CSInterface object + */ +function CSInterface() +{ +} + +/** + * User can add this event listener to handle native application theme color changes. + * Callback function gives extensions ability to fine-tune their theme color after the + * global theme color has been changed. + * The callback function should be like below: + * + * @example + * // event is a CSEvent object, but user can ignore it. + * function OnAppThemeColorChanged(event) + * { + * // Should get a latest HostEnvironment object from application. + * var skinInfo = JSON.parse(window.__adobe_cep__.getHostEnvironment()).appSkinInfo; + * // Gets the style information such as color info from the skinInfo, + * // and redraw all UI controls of your extension according to the style info. + * } + */ +CSInterface.THEME_COLOR_CHANGED_EVENT = "com.adobe.csxs.events.ThemeColorChanged"; + +/** The host environment data object. */ +CSInterface.prototype.hostEnvironment = window.__adobe_cep__ ? JSON.parse(window.__adobe_cep__.getHostEnvironment()) : null; + +/** Retrieves information about the host environment in which the + * extension is currently running. + * + * @return A \c #HostEnvironment object. + */ +CSInterface.prototype.getHostEnvironment = function() +{ + this.hostEnvironment = JSON.parse(window.__adobe_cep__.getHostEnvironment()); + return this.hostEnvironment; +}; + +/** Closes this extension. */ +CSInterface.prototype.closeExtension = function() +{ + window.__adobe_cep__.closeExtension(); +}; + +/** + * Retrieves a path for which a constant is defined in the system. + * + * @param pathType The path-type constant defined in \c #SystemPath , + * + * @return The platform-specific system path string. + */ +CSInterface.prototype.getSystemPath = function(pathType) +{ + var path = decodeURI(window.__adobe_cep__.getSystemPath(pathType)); + var OSVersion = this.getOSInformation(); + if (OSVersion.indexOf("Windows") >= 0) + { + path = path.replace("file:///", ""); + } + else if (OSVersion.indexOf("Mac") >= 0) + { + path = path.replace("file://", ""); + } + return path; +}; + +/** + * Evaluates a JavaScript script, which can use the JavaScript DOM + * of the host application. + * + * @param script The JavaScript script. + * @param callback Optional. A callback function that receives the result of execution. + * If execution fails, the callback function receives the error message \c EvalScript_ErrMessage. + */ +CSInterface.prototype.evalScript = function(script, callback) +{ + if(callback === null || callback === undefined) + { + callback = function(result){}; + } + window.__adobe_cep__.evalScript(script, callback); +}; + +/** + * Retrieves the unique identifier of the application. + * in which the extension is currently running. + * + * @return The unique ID string. + */ +CSInterface.prototype.getApplicationID = function() +{ + var appId = this.hostEnvironment.appId; + return appId; +}; + +/** + * Retrieves host capability information for the application + * in which the extension is currently running. + * + * @return A \c #HostCapabilities object. + */ +CSInterface.prototype.getHostCapabilities = function() +{ + var hostCapabilities = JSON.parse(window.__adobe_cep__.getHostCapabilities() ); + return hostCapabilities; +}; + +/** + * Triggers a CEP event programmatically. Yoy can use it to dispatch + * an event of a predefined type, or of a type you have defined. + * + * @param event A \c CSEvent object. + */ +CSInterface.prototype.dispatchEvent = function(event) +{ + if (typeof event.data == "object") + { + event.data = JSON.stringify(event.data); + } + + window.__adobe_cep__.dispatchEvent(event); +}; + +/** + * Registers an interest in a CEP event of a particular type, and + * assigns an event handler. + * The event infrastructure notifies your extension when events of this type occur, + * passing the event object to the registered handler function. + * + * @param type The name of the event type of interest. + * @param listener The JavaScript handler function or method. + * @param obj Optional, the object containing the handler method, if any. + * Default is null. + */ +CSInterface.prototype.addEventListener = function(type, listener, obj) +{ + window.__adobe_cep__.addEventListener(type, listener, obj); +}; + +/** + * Removes a registered event listener. + * + * @param type The name of the event type of interest. + * @param listener The JavaScript handler function or method that was registered. + * @param obj Optional, the object containing the handler method, if any. + * Default is null. + */ +CSInterface.prototype.removeEventListener = function(type, listener, obj) +{ + window.__adobe_cep__.removeEventListener(type, listener, obj); +}; + +/** + * Loads and launches another extension, or activates the extension if it is already loaded. + * + * @param extensionId The extension's unique identifier. + * @param startupParams Not currently used, pass "". + * + * @example + * To launch the extension "help" with ID "HLP" from this extension, call: + * requestOpenExtension("HLP", ""); + * + */ +CSInterface.prototype.requestOpenExtension = function(extensionId, params) +{ + window.__adobe_cep__.requestOpenExtension(extensionId, params); +}; + +/** + * Retrieves the list of extensions currently loaded in the current host application. + * The extension list is initialized once, and remains the same during the lifetime + * of the CEP session. + * + * @param extensionIds Optional, an array of unique identifiers for extensions of interest. + * If omitted, retrieves data for all extensions. + * + * @return Zero or more \c #Extension objects. + */ +CSInterface.prototype.getExtensions = function(extensionIds) +{ + var extensionIdsStr = JSON.stringify(extensionIds); + var extensionsStr = window.__adobe_cep__.getExtensions(extensionIdsStr); + + var extensions = JSON.parse(extensionsStr); + return extensions; +}; + +/** + * Retrieves network-related preferences. + * + * @return A JavaScript object containing network preferences. + */ +CSInterface.prototype.getNetworkPreferences = function() +{ + var result = window.__adobe_cep__.getNetworkPreferences(); + var networkPre = JSON.parse(result); + + return networkPre; +}; + +/** + * Initializes the resource bundle for this extension with property values + * for the current application and locale. + * To support multiple locales, you must define a property file for each locale, + * containing keyed display-string values for that locale. + * See localization documentation for Extension Builder and related products. + * + * Keys can be in the + * form key.value="localized string", for use in HTML text elements. + * For example, in this input element, the localized \c key.value string is displayed + * instead of the empty \c value string: + * + * + * + * @return An object containing the resource bundle information. + */ +CSInterface.prototype.initResourceBundle = function() +{ + var resourceBundle = JSON.parse(window.__adobe_cep__.initResourceBundle()); + var resElms = document.querySelectorAll('[data-locale]'); + for (var n = 0; n < resElms.length; n++) + { + var resEl = resElms[n]; + // Get the resource key from the element. + var resKey = resEl.getAttribute('data-locale'); + if (resKey) + { + // Get all the resources that start with the key. + for (var key in resourceBundle) + { + if (key.indexOf(resKey) === 0) + { + var resValue = resourceBundle[key]; + if (key.length == resKey.length) + { + resEl.innerHTML = resValue; + } + else if ('.' == key.charAt(resKey.length)) + { + var attrKey = key.substring(resKey.length + 1); + resEl[attrKey] = resValue; + } + } + } + } + } + return resourceBundle; +}; + +/** + * Writes installation information to a file. + * + * @return The file path. + */ +CSInterface.prototype.dumpInstallationInfo = function() +{ + return window.__adobe_cep__.dumpInstallationInfo(); +}; + +/** + * Retrieves version information for the current Operating System, + * See http://www.useragentstring.com/pages/Chrome/ for Chrome \c navigator.userAgent values. + * + * @return A string containing the OS version, or "unknown Operation System". + * If user customizes the User Agent by setting CEF command parameter "--user-agent", only + * "Mac OS X" or "Windows" will be returned. + */ +CSInterface.prototype.getOSInformation = function() +{ + var userAgent = navigator.userAgent; + + if ((navigator.platform == "Win32") || (navigator.platform == "Windows")) + { + var winVersion = "Windows"; + var winBit = ""; + if (userAgent.indexOf("Windows") > -1) + { + if (userAgent.indexOf("Windows NT 5.0") > -1) + { + winVersion = "Windows 2000"; + } + else if (userAgent.indexOf("Windows NT 5.1") > -1) + { + winVersion = "Windows XP"; + } + else if (userAgent.indexOf("Windows NT 5.2") > -1) + { + winVersion = "Windows Server 2003"; + } + else if (userAgent.indexOf("Windows NT 6.0") > -1) + { + winVersion = "Windows Vista"; + } + else if (userAgent.indexOf("Windows NT 6.1") > -1) + { + winVersion = "Windows 7"; + } + else if (userAgent.indexOf("Windows NT 6.2") > -1) + { + winVersion = "Windows 8"; + } + else if (userAgent.indexOf("Windows NT 6.3") > -1) + { + winVersion = "Windows 8.1"; + } + else if (userAgent.indexOf("Windows NT 10") > -1) + { + winVersion = "Windows 10"; + } + + if (userAgent.indexOf("WOW64") > -1 || userAgent.indexOf("Win64") > -1) + { + winBit = " 64-bit"; + } + else + { + winBit = " 32-bit"; + } + } + + return winVersion + winBit; + } + else if ((navigator.platform == "MacIntel") || (navigator.platform == "Macintosh")) + { + var result = "Mac OS X"; + + if (userAgent.indexOf("Mac OS X") > -1) + { + result = userAgent.substring(userAgent.indexOf("Mac OS X"), userAgent.indexOf(")")); + result = result.replace(/_/g, "."); + } + + return result; + } + + return "Unknown Operation System"; +}; + +/** + * Opens a page in the default system browser. + * + * Since 4.2.0 + * + * @param url The URL of the page/file to open, or the email address. + * Must use HTTP/HTTPS/file/mailto protocol. For example: + * "http://www.adobe.com" + * "https://github.com" + * "file:///C:/log.txt" + * "mailto:test@adobe.com" + * + * @return One of these error codes:\n + *
    \n + *
  • NO_ERROR - 0
  • \n + *
  • ERR_UNKNOWN - 1
  • \n + *
  • ERR_INVALID_PARAMS - 2
  • \n + *
  • ERR_INVALID_URL - 201
  • \n + *
\n + */ +CSInterface.prototype.openURLInDefaultBrowser = function(url) +{ + return cep.util.openURLInDefaultBrowser(url); +}; + +/** + * Retrieves extension ID. + * + * Since 4.2.0 + * + * @return extension ID. + */ +CSInterface.prototype.getExtensionID = function() +{ + return window.__adobe_cep__.getExtensionId(); +}; + +/** + * Retrieves the scale factor of screen. + * On Windows platform, the value of scale factor might be different from operating system's scale factor, + * since host application may use its self-defined scale factor. + * + * Since 4.2.0 + * + * @return One of the following float number. + *
    \n + *
  • -1.0 when error occurs
  • \n + *
  • 1.0 means normal screen
  • \n + *
  • >1.0 means HiDPI screen
  • \n + *
\n + */ +CSInterface.prototype.getScaleFactor = function() +{ + return window.__adobe_cep__.getScaleFactor(); +}; + +/** + * Set a handler to detect any changes of scale factor. This only works on Mac. + * + * Since 4.2.0 + * + * @param handler The function to be called when scale factor is changed. + * + */ +CSInterface.prototype.setScaleFactorChangedHandler = function(handler) +{ + window.__adobe_cep__.setScaleFactorChangedHandler(handler); +}; + +/** + * Retrieves current API version. + * + * Since 4.2.0 + * + * @return ApiVersion object. + * + */ +CSInterface.prototype.getCurrentApiVersion = function() +{ + var apiVersion = JSON.parse(window.__adobe_cep__.getCurrentApiVersion()); + return apiVersion; +}; + +/** + * Set panel flyout menu by an XML. + * + * Since 5.2.0 + * + * Register a callback function for "com.adobe.csxs.events.flyoutMenuClicked" to get notified when a + * menu item is clicked. + * The "data" attribute of event is an object which contains "menuId" and "menuName" attributes. + * + * Register callback functions for "com.adobe.csxs.events.flyoutMenuOpened" and "com.adobe.csxs.events.flyoutMenuClosed" + * respectively to get notified when flyout menu is opened or closed. + * + * @param menu A XML string which describes menu structure. + * An example menu XML: + * + * + * + * + * + * + * + * + * + * + * + * + */ +CSInterface.prototype.setPanelFlyoutMenu = function(menu) +{ + if ("string" != typeof menu) + { + return; + } + + window.__adobe_cep__.invokeSync("setPanelFlyoutMenu", menu); +}; + +/** + * Updates a menu item in the extension window's flyout menu, by setting the enabled + * and selection status. + * + * Since 5.2.0 + * + * @param menuItemLabel The menu item label. + * @param enabled True to enable the item, false to disable it (gray it out). + * @param checked True to select the item, false to deselect it. + * + * @return false when the host application does not support this functionality (HostCapabilities.EXTENDED_PANEL_MENU is false). + * Fails silently if menu label is invalid. + * + * @see HostCapabilities.EXTENDED_PANEL_MENU + */ +CSInterface.prototype.updatePanelMenuItem = function(menuItemLabel, enabled, checked) +{ + var ret = false; + if (this.getHostCapabilities().EXTENDED_PANEL_MENU) + { + var itemStatus = new MenuItemStatus(menuItemLabel, enabled, checked); + ret = window.__adobe_cep__.invokeSync("updatePanelMenuItem", JSON.stringify(itemStatus)); + } + return ret; +}; + + +/** + * Set context menu by XML string. + * + * Since 5.2.0 + * + * There are a number of conventions used to communicate what type of menu item to create and how it should be handled. + * - an item without menu ID or menu name is disabled and is not shown. + * - if the item name is "---" (three hyphens) then it is treated as a separator. The menu ID in this case will always be NULL. + * - Checkable attribute takes precedence over Checked attribute. + * - a PNG icon. For optimal display results please supply a 16 x 16px icon as larger dimensions will increase the size of the menu item. + The Chrome extension contextMenus API was taken as a reference. + https://developer.chrome.com/extensions/contextMenus + * - the items with icons and checkable items cannot coexist on the same menu level. The former take precedences over the latter. + * + * @param menu A XML string which describes menu structure. + * @param callback The callback function which is called when a menu item is clicked. The only parameter is the returned ID of clicked menu item. + * + * @description An example menu XML: + * + * + * + * + * + * + * + * + * + * + * + */ +CSInterface.prototype.setContextMenu = function(menu, callback) +{ + if ("string" != typeof menu) + { + return; + } + + window.__adobe_cep__.invokeAsync("setContextMenu", menu, callback); +}; + +/** + * Set context menu by JSON string. + * + * Since 6.0.0 + * + * There are a number of conventions used to communicate what type of menu item to create and how it should be handled. + * - an item without menu ID or menu name is disabled and is not shown. + * - if the item label is "---" (three hyphens) then it is treated as a separator. The menu ID in this case will always be NULL. + * - Checkable attribute takes precedence over Checked attribute. + * - a PNG icon. For optimal display results please supply a 16 x 16px icon as larger dimensions will increase the size of the menu item. + The Chrome extension contextMenus API was taken as a reference. + * - the items with icons and checkable items cannot coexist on the same menu level. The former take precedences over the latter. + https://developer.chrome.com/extensions/contextMenus + * + * @param menu A JSON string which describes menu structure. + * @param callback The callback function which is called when a menu item is clicked. The only parameter is the returned ID of clicked menu item. + * + * @description An example menu JSON: + * + * { + * "menu": [ + * { + * "id": "menuItemId1", + * "label": "testExample1", + * "enabled": true, + * "checkable": true, + * "checked": false, + * "icon": "./image/small_16X16.png" + * }, + * { + * "id": "menuItemId2", + * "label": "testExample2", + * "menu": [ + * { + * "id": "menuItemId2-1", + * "label": "testExample2-1", + * "menu": [ + * { + * "id": "menuItemId2-1-1", + * "label": "testExample2-1-1", + * "enabled": false, + * "checkable": true, + * "checked": true + * } + * ] + * }, + * { + * "id": "menuItemId2-2", + * "label": "testExample2-2", + * "enabled": true, + * "checkable": true, + * "checked": true + * } + * ] + * }, + * { + * "label": "---" + * }, + * { + * "id": "menuItemId3", + * "label": "testExample3", + * "enabled": false, + * "checkable": true, + * "checked": false + * } + * ] + * } + * + */ +CSInterface.prototype.setContextMenuByJSON = function(menu, callback) +{ + if ("string" != typeof menu) + { + return; + } + + window.__adobe_cep__.invokeAsync("setContextMenuByJSON", menu, callback); +}; + +/** + * Updates a context menu item by setting the enabled and selection status. + * + * Since 5.2.0 + * + * @param menuItemID The menu item ID. + * @param enabled True to enable the item, false to disable it (gray it out). + * @param checked True to select the item, false to deselect it. + */ +CSInterface.prototype.updateContextMenuItem = function(menuItemID, enabled, checked) +{ + var itemStatus = new ContextMenuItemStatus(menuItemID, enabled, checked); + ret = window.__adobe_cep__.invokeSync("updateContextMenuItem", JSON.stringify(itemStatus)); +}; + +/** + * Get the visibility status of an extension window. + * + * Since 6.0.0 + * + * @return true if the extension window is visible; false if the extension window is hidden. + */ +CSInterface.prototype.isWindowVisible = function() +{ + return window.__adobe_cep__.invokeSync("isWindowVisible", ""); +}; + +/** + * Resize extension's content to the specified dimensions. + * 1. Works with modal and modeless extensions in all Adobe products. + * 2. Extension's manifest min/max size constraints apply and take precedence. + * 3. For panel extensions + * 3.1 This works in all Adobe products except: + * * Premiere Pro + * * Prelude + * * After Effects + * 3.2 When the panel is in certain states (especially when being docked), + * it will not change to the desired dimensions even when the + * specified size satisfies min/max constraints. + * + * Since 6.0.0 + * + * @param width The new width + * @param height The new height + */ +CSInterface.prototype.resizeContent = function(width, height) +{ + window.__adobe_cep__.resizeContent(width, height); +}; + +/** + * Register the invalid certificate callback for an extension. + * This callback will be triggered when the extension tries to access the web site that contains the invalid certificate on the main frame. + * But if the extension does not call this function and tries to access the web site containing the invalid certificate, a default error page will be shown. + * + * Since 6.1.0 + * + * @param callback the callback function + */ +CSInterface.prototype.registerInvalidCertificateCallback = function(callback) +{ + return window.__adobe_cep__.registerInvalidCertificateCallback(callback); +}; + +/** + * Register an interest in some key events to prevent them from being sent to the host application. + * + * This function works with modeless extensions and panel extensions. + * Generally all the key events will be sent to the host application for these two extensions if the current focused element + * is not text input or dropdown, + * If you want to intercept some key events and want them to be handled in the extension, please call this function + * in advance to prevent them being sent to the host application. + * + * Since 6.1.0 + * + * @param keyEventsInterest A JSON string describing those key events you are interested in. A null object or + an empty string will lead to removing the interest + * + * This JSON string should be an array, each object has following keys: + * + * keyCode: [Required] represents an OS system dependent virtual key code identifying + * the unmodified value of the pressed key. + * ctrlKey: [optional] a Boolean that indicates if the control key was pressed (true) or not (false) when the event occurred. + * altKey: [optional] a Boolean that indicates if the alt key was pressed (true) or not (false) when the event occurred. + * shiftKey: [optional] a Boolean that indicates if the shift key was pressed (true) or not (false) when the event occurred. + * metaKey: [optional] (Mac Only) a Boolean that indicates if the Meta key was pressed (true) or not (false) when the event occurred. + * On Macintosh keyboards, this is the command key. To detect Windows key on Windows, please use keyCode instead. + * An example JSON string: + * + * [ + * { + * "keyCode": 48 + * }, + * { + * "keyCode": 123, + * "ctrlKey": true + * }, + * { + * "keyCode": 123, + * "ctrlKey": true, + * "metaKey": true + * } + * ] + * + */ +CSInterface.prototype.registerKeyEventsInterest = function(keyEventsInterest) +{ + return window.__adobe_cep__.registerKeyEventsInterest(keyEventsInterest); +}; + +/** + * Set the title of the extension window. + * This function works with modal and modeless extensions in all Adobe products, and panel extensions in Photoshop, InDesign, InCopy, Illustrator, Flash Pro and Dreamweaver. + * + * Since 6.1.0 + * + * @param title The window title. + */ +CSInterface.prototype.setWindowTitle = function(title) +{ + window.__adobe_cep__.invokeSync("setWindowTitle", title); +}; + +/** + * Get the title of the extension window. + * This function works with modal and modeless extensions in all Adobe products, and panel extensions in Photoshop, InDesign, InCopy, Illustrator, Flash Pro and Dreamweaver. + * + * Since 6.1.0 + * + * @return The window title. + */ +CSInterface.prototype.getWindowTitle = function() +{ + return window.__adobe_cep__.invokeSync("getWindowTitle", ""); +}; diff --git a/openpype/hosts/aftereffects/api/extension/js/libs/jquery-2.0.2.min.js b/openpype/hosts/aftereffects/api/extension/js/libs/jquery-2.0.2.min.js new file mode 100644 index 00000000000..73e5218d210 --- /dev/null +++ b/openpype/hosts/aftereffects/api/extension/js/libs/jquery-2.0.2.min.js @@ -0,0 +1,6 @@ +/*! jQuery v2.0.2 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license +//@ sourceMappingURL=jquery-2.0.2.min.map +*/ +(function(e,undefined){var t,n,r=typeof undefined,i=e.location,o=e.document,s=o.documentElement,a=e.jQuery,u=e.$,l={},c=[],p="2.0.2",f=c.concat,h=c.push,d=c.slice,g=c.indexOf,m=l.toString,y=l.hasOwnProperty,v=p.trim,x=function(e,n){return new x.fn.init(e,n,t)},b=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,w=/\S+/g,T=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,k=/^-ms-/,N=/-([\da-z])/gi,E=function(e,t){return t.toUpperCase()},S=function(){o.removeEventListener("DOMContentLoaded",S,!1),e.removeEventListener("load",S,!1),x.ready()};x.fn=x.prototype={jquery:p,constructor:x,init:function(e,t,n){var r,i;if(!e)return this;if("string"==typeof e){if(r="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:T.exec(e),!r||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof x?t[0]:t,x.merge(this,x.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:o,!0)),C.test(r[1])&&x.isPlainObject(t))for(r in t)x.isFunction(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return i=o.getElementById(r[2]),i&&i.parentNode&&(this.length=1,this[0]=i),this.context=o,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?n.ready(e):(e.selector!==undefined&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return d.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,t,n,r,i,o,s=arguments[0]||{},a=1,u=arguments.length,l=!1;for("boolean"==typeof s&&(l=s,s=arguments[1]||{},a=2),"object"==typeof s||x.isFunction(s)||(s={}),u===a&&(s=this,--a);u>a;a++)if(null!=(e=arguments[a]))for(t in e)n=s[t],r=e[t],s!==r&&(l&&r&&(x.isPlainObject(r)||(i=x.isArray(r)))?(i?(i=!1,o=n&&x.isArray(n)?n:[]):o=n&&x.isPlainObject(n)?n:{},s[t]=x.extend(l,o,r)):r!==undefined&&(s[t]=r));return s},x.extend({expando:"jQuery"+(p+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=a),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){(e===!0?--x.readyWait:x.isReady)||(x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(o,[x]),x.fn.trigger&&x(o).trigger("ready").off("ready")))},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray,isWindow:function(e){return null!=e&&e===e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[m.call(e)]||"object":typeof e},isPlainObject:function(e){if("object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!y.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(t){return!1}return!0},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||o;var r=C.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:JSON.parse,parseXML:function(e){var t,n;if(!e||"string"!=typeof e)return null;try{n=new DOMParser,t=n.parseFromString(e,"text/xml")}catch(r){t=undefined}return(!t||t.getElementsByTagName("parsererror").length)&&x.error("Invalid XML: "+e),t},noop:function(){},globalEval:function(e){var t,n=eval;e=x.trim(e),e&&(1===e.indexOf("use strict")?(t=o.createElement("script"),t.text=e,o.head.appendChild(t).parentNode.removeChild(t)):n(e))},camelCase:function(e){return e.replace(k,"ms-").replace(N,E)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,s=j(e);if(n){if(s){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(s){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:function(e){return null==e?"":v.call(e)},makeArray:function(e,t){var n=t||[];return null!=e&&(j(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:g.call(t,e,n)},merge:function(e,t){var n=t.length,r=e.length,i=0;if("number"==typeof n)for(;n>i;i++)e[r++]=t[i];else while(t[i]!==undefined)e[r++]=t[i++];return e.length=r,e},grep:function(e,t,n){var r,i=[],o=0,s=e.length;for(n=!!n;s>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,s=j(e),a=[];if(s)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(a[a.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(a[a.length]=r);return f.apply([],a)},guid:1,proxy:function(e,t){var n,r,i;return"string"==typeof t&&(n=e[t],t=e,e=n),x.isFunction(e)?(r=d.call(arguments,2),i=function(){return e.apply(t||this,r.concat(d.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):undefined},access:function(e,t,n,r,i,o,s){var a=0,u=e.length,l=null==n;if("object"===x.type(n)){i=!0;for(a in n)x.access(e,t,a,n[a],!0,o,s)}else if(r!==undefined&&(i=!0,x.isFunction(r)||(s=!0),l&&(s?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(x(e),n)})),t))for(;u>a;a++)t(e[a],n,s?r:r.call(e[a],a,t(e[a],n)));return i?e:l?t.call(e):u?t(e[0],n):o},now:Date.now,swap:function(e,t,n,r){var i,o,s={};for(o in t)s[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=s[o];return i}}),x.ready.promise=function(t){return n||(n=x.Deferred(),"complete"===o.readyState?setTimeout(x.ready):(o.addEventListener("DOMContentLoaded",S,!1),e.addEventListener("load",S,!1))),n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){l["[object "+t+"]"]=t.toLowerCase()});function j(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}t=x(o),function(e,undefined){var t,n,r,i,o,s,a,u,l,c,p,f,h,d,g,m,y,v="sizzle"+-new Date,b=e.document,w=0,T=0,C=at(),k=at(),N=at(),E=!1,S=function(){return 0},j=typeof undefined,D=1<<31,A={}.hasOwnProperty,L=[],H=L.pop,q=L.push,O=L.push,F=L.slice,P=L.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},R="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",W="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",$=W.replace("w","w#"),B="\\["+M+"*("+W+")"+M+"*(?:([*^$|!~]?=)"+M+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+$+")|)|)"+M+"*\\]",I=":("+W+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+B.replace(3,8)+")*)|.*)\\)|)",z=RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),_=RegExp("^"+M+"*,"+M+"*"),X=RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=RegExp(M+"*[+~]"),Y=RegExp("="+M+"*([^\\]'\"]*)"+M+"*\\]","g"),V=RegExp(I),G=RegExp("^"+$+"$"),J={ID:RegExp("^#("+W+")"),CLASS:RegExp("^\\.("+W+")"),TAG:RegExp("^("+W.replace("w","w*")+")"),ATTR:RegExp("^"+B),PSEUDO:RegExp("^"+I),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:RegExp("^(?:"+R+")$","i"),needsContext:RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Q=/^[^{]+\{\s*\[native \w/,K=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,Z=/^(?:input|select|textarea|button)$/i,et=/^h\d$/i,tt=/'|\\/g,nt=RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),rt=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(55296|r>>10,56320|1023&r)};try{O.apply(L=F.call(b.childNodes),b.childNodes),L[b.childNodes.length].nodeType}catch(it){O={apply:L.length?function(e,t){q.apply(e,F.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function ot(e,t,r,i){var o,s,a,u,l,f,g,m,x,w;if((t?t.ownerDocument||t:b)!==p&&c(t),t=t||p,r=r||[],!e||"string"!=typeof e)return r;if(1!==(u=t.nodeType)&&9!==u)return[];if(h&&!i){if(o=K.exec(e))if(a=o[1]){if(9===u){if(s=t.getElementById(a),!s||!s.parentNode)return r;if(s.id===a)return r.push(s),r}else if(t.ownerDocument&&(s=t.ownerDocument.getElementById(a))&&y(t,s)&&s.id===a)return r.push(s),r}else{if(o[2])return O.apply(r,t.getElementsByTagName(e)),r;if((a=o[3])&&n.getElementsByClassName&&t.getElementsByClassName)return O.apply(r,t.getElementsByClassName(a)),r}if(n.qsa&&(!d||!d.test(e))){if(m=g=v,x=t,w=9===u&&e,1===u&&"object"!==t.nodeName.toLowerCase()){f=vt(e),(g=t.getAttribute("id"))?m=g.replace(tt,"\\$&"):t.setAttribute("id",m),m="[id='"+m+"'] ",l=f.length;while(l--)f[l]=m+xt(f[l]);x=U.test(e)&&t.parentNode||t,w=f.join(",")}if(w)try{return O.apply(r,x.querySelectorAll(w)),r}catch(T){}finally{g||t.removeAttribute("id")}}}return St(e.replace(z,"$1"),t,r,i)}function st(e){return Q.test(e+"")}function at(){var e=[];function t(n,r){return e.push(n+=" ")>i.cacheLength&&delete t[e.shift()],t[n]=r}return t}function ut(e){return e[v]=!0,e}function lt(e){var t=p.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function ct(e,t,n){e=e.split("|");var r,o=e.length,s=n?null:t;while(o--)(r=i.attrHandle[e[o]])&&r!==t||(i.attrHandle[e[o]]=s)}function pt(e,t){var n=e.getAttributeNode(t);return n&&n.specified?n.value:e[t]===!0?t.toLowerCase():null}function ft(e,t){return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}function ht(e){return"input"===e.nodeName.toLowerCase()?e.defaultValue:undefined}function dt(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function gt(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function mt(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function yt(e){return ut(function(t){return t=+t,ut(function(n,r){var i,o=e([],n.length,t),s=o.length;while(s--)n[i=o[s]]&&(n[i]=!(r[i]=n[i]))})})}s=ot.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},n=ot.support={},c=ot.setDocument=function(e){var t=e?e.ownerDocument||e:b,r=t.parentWindow;return t!==p&&9===t.nodeType&&t.documentElement?(p=t,f=t.documentElement,h=!s(t),r&&r.frameElement&&r.attachEvent("onbeforeunload",function(){c()}),n.attributes=lt(function(e){return e.innerHTML="",ct("type|href|height|width",ft,"#"===e.firstChild.getAttribute("href")),ct(R,pt,null==e.getAttribute("disabled")),e.className="i",!e.getAttribute("className")}),n.input=lt(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")}),ct("value",ht,n.attributes&&n.input),n.getElementsByTagName=lt(function(e){return e.appendChild(t.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=lt(function(e){return e.innerHTML="
",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),n.getById=lt(function(e){return f.appendChild(e).id=v,!t.getElementsByName||!t.getElementsByName(v).length}),n.getById?(i.find.ID=function(e,t){if(typeof t.getElementById!==j&&h){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},i.filter.ID=function(e){var t=e.replace(nt,rt);return function(e){return e.getAttribute("id")===t}}):(delete i.find.ID,i.filter.ID=function(e){var t=e.replace(nt,rt);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),i.find.TAG=n.getElementsByTagName?function(e,t){return typeof t.getElementsByTagName!==j?t.getElementsByTagName(e):undefined}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},i.find.CLASS=n.getElementsByClassName&&function(e,t){return typeof t.getElementsByClassName!==j&&h?t.getElementsByClassName(e):undefined},g=[],d=[],(n.qsa=st(t.querySelectorAll))&&(lt(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||d.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll(":checked").length||d.push(":checked")}),lt(function(e){var n=t.createElement("input");n.setAttribute("type","hidden"),e.appendChild(n).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&d.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||d.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),d.push(",.*:")})),(n.matchesSelector=st(m=f.webkitMatchesSelector||f.mozMatchesSelector||f.oMatchesSelector||f.msMatchesSelector))&<(function(e){n.disconnectedMatch=m.call(e,"div"),m.call(e,"[s!='']:x"),g.push("!=",I)}),d=d.length&&RegExp(d.join("|")),g=g.length&&RegExp(g.join("|")),y=st(f.contains)||f.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},n.sortDetached=lt(function(e){return 1&e.compareDocumentPosition(t.createElement("div"))}),S=f.compareDocumentPosition?function(e,r){if(e===r)return E=!0,0;var i=r.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(r);return i?1&i||!n.sortDetached&&r.compareDocumentPosition(e)===i?e===t||y(b,e)?-1:r===t||y(b,r)?1:l?P.call(l,e)-P.call(l,r):0:4&i?-1:1:e.compareDocumentPosition?-1:1}:function(e,n){var r,i=0,o=e.parentNode,s=n.parentNode,a=[e],u=[n];if(e===n)return E=!0,0;if(!o||!s)return e===t?-1:n===t?1:o?-1:s?1:l?P.call(l,e)-P.call(l,n):0;if(o===s)return dt(e,n);r=e;while(r=r.parentNode)a.unshift(r);r=n;while(r=r.parentNode)u.unshift(r);while(a[i]===u[i])i++;return i?dt(a[i],u[i]):a[i]===b?-1:u[i]===b?1:0},t):p},ot.matches=function(e,t){return ot(e,null,null,t)},ot.matchesSelector=function(e,t){if((e.ownerDocument||e)!==p&&c(e),t=t.replace(Y,"='$1']"),!(!n.matchesSelector||!h||g&&g.test(t)||d&&d.test(t)))try{var r=m.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(i){}return ot(t,p,null,[e]).length>0},ot.contains=function(e,t){return(e.ownerDocument||e)!==p&&c(e),y(e,t)},ot.attr=function(e,t){(e.ownerDocument||e)!==p&&c(e);var r=i.attrHandle[t.toLowerCase()],o=r&&A.call(i.attrHandle,t.toLowerCase())?r(e,t,!h):undefined;return o===undefined?n.attributes||!h?e.getAttribute(t):(o=e.getAttributeNode(t))&&o.specified?o.value:null:o},ot.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},ot.uniqueSort=function(e){var t,r=[],i=0,o=0;if(E=!n.detectDuplicates,l=!n.sortStable&&e.slice(0),e.sort(S),E){while(t=e[o++])t===e[o]&&(i=r.push(o));while(i--)e.splice(r[i],1)}return e},o=ot.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=o(t);return n},i=ot.selectors={cacheLength:50,createPseudo:ut,match:J,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(nt,rt),e[3]=(e[4]||e[5]||"").replace(nt,rt),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||ot.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&ot.error(e[0]),e},PSEUDO:function(e){var t,n=!e[5]&&e[2];return J.CHILD.test(e[0])?null:(e[3]&&e[4]!==undefined?e[2]=e[4]:n&&V.test(n)&&(t=vt(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(nt,rt).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=C[e+" "];return t||(t=RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&C(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=ot.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),s="last"!==e.slice(-4),a="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,p,f,h,d,g=o!==s?"nextSibling":"previousSibling",m=t.parentNode,y=a&&t.nodeName.toLowerCase(),x=!u&&!a;if(m){if(o){while(g){p=t;while(p=p[g])if(a?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;d=g="only"===e&&!d&&"nextSibling"}return!0}if(d=[s?m.firstChild:m.lastChild],s&&x){c=m[v]||(m[v]={}),l=c[e]||[],h=l[0]===w&&l[1],f=l[0]===w&&l[2],p=h&&m.childNodes[h];while(p=++h&&p&&p[g]||(f=h=0)||d.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[w,h,f];break}}else if(x&&(l=(t[v]||(t[v]={}))[e])&&l[0]===w)f=l[1];else while(p=++h&&p&&p[g]||(f=h=0)||d.pop())if((a?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(x&&((p[v]||(p[v]={}))[e]=[w,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||ot.error("unsupported pseudo: "+e);return r[v]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?ut(function(e,n){var i,o=r(e,t),s=o.length;while(s--)i=P.call(e,o[s]),e[i]=!(n[i]=o[s])}):function(e){return r(e,0,n)}):r}},pseudos:{not:ut(function(e){var t=[],n=[],r=a(e.replace(z,"$1"));return r[v]?ut(function(e,t,n,i){var o,s=r(e,null,i,[]),a=e.length;while(a--)(o=s[a])&&(e[a]=!(t[a]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:ut(function(e){return function(t){return ot(e,t).length>0}}),contains:ut(function(e){return function(t){return(t.textContent||t.innerText||o(t)).indexOf(e)>-1}}),lang:ut(function(e){return G.test(e||"")||ot.error("unsupported lang: "+e),e=e.replace(nt,rt).toLowerCase(),function(t){var n;do if(n=h?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===f},focus:function(e){return e===p.activeElement&&(!p.hasFocus||p.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!i.pseudos.empty(e)},header:function(e){return et.test(e.nodeName)},input:function(e){return Z.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:yt(function(){return[0]}),last:yt(function(e,t){return[t-1]}),eq:yt(function(e,t,n){return[0>n?n+t:n]}),even:yt(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:yt(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:yt(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:yt(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}};for(t in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})i.pseudos[t]=gt(t);for(t in{submit:!0,reset:!0})i.pseudos[t]=mt(t);function vt(e,t){var n,r,o,s,a,u,l,c=k[e+" "];if(c)return t?0:c.slice(0);a=e,u=[],l=i.preFilter;while(a){(!n||(r=_.exec(a)))&&(r&&(a=a.slice(r[0].length)||a),u.push(o=[])),n=!1,(r=X.exec(a))&&(n=r.shift(),o.push({value:n,type:r[0].replace(z," ")}),a=a.slice(n.length));for(s in i.filter)!(r=J[s].exec(a))||l[s]&&!(r=l[s](r))||(n=r.shift(),o.push({value:n,type:s,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?ot.error(e):k(e,u).slice(0)}function xt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function bt(e,t,n){var i=t.dir,o=n&&"parentNode"===i,s=T++;return t.first?function(t,n,r){while(t=t[i])if(1===t.nodeType||o)return e(t,n,r)}:function(t,n,a){var u,l,c,p=w+" "+s;if(a){while(t=t[i])if((1===t.nodeType||o)&&e(t,n,a))return!0}else while(t=t[i])if(1===t.nodeType||o)if(c=t[v]||(t[v]={}),(l=c[i])&&l[0]===p){if((u=l[1])===!0||u===r)return u===!0}else if(l=c[i]=[p],l[1]=e(t,n,a)||r,l[1]===!0)return!0}}function wt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function Tt(e,t,n,r,i){var o,s=[],a=0,u=e.length,l=null!=t;for(;u>a;a++)(o=e[a])&&(!n||n(o,r,i))&&(s.push(o),l&&t.push(a));return s}function Ct(e,t,n,r,i,o){return r&&!r[v]&&(r=Ct(r)),i&&!i[v]&&(i=Ct(i,o)),ut(function(o,s,a,u){var l,c,p,f=[],h=[],d=s.length,g=o||Et(t||"*",a.nodeType?[a]:a,[]),m=!e||!o&&t?g:Tt(g,f,e,a,u),y=n?i||(o?e:d||r)?[]:s:m;if(n&&n(m,y,a,u),r){l=Tt(y,h),r(l,[],a,u),c=l.length;while(c--)(p=l[c])&&(y[h[c]]=!(m[h[c]]=p))}if(o){if(i||e){if(i){l=[],c=y.length;while(c--)(p=y[c])&&l.push(m[c]=p);i(null,y=[],l,u)}c=y.length;while(c--)(p=y[c])&&(l=i?P.call(o,p):f[c])>-1&&(o[l]=!(s[l]=p))}}else y=Tt(y===s?y.splice(d,y.length):y),i?i(null,s,y,u):O.apply(s,y)})}function kt(e){var t,n,r,o=e.length,s=i.relative[e[0].type],a=s||i.relative[" "],l=s?1:0,c=bt(function(e){return e===t},a,!0),p=bt(function(e){return P.call(t,e)>-1},a,!0),f=[function(e,n,r){return!s&&(r||n!==u)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;o>l;l++)if(n=i.relative[e[l].type])f=[bt(wt(f),n)];else{if(n=i.filter[e[l].type].apply(null,e[l].matches),n[v]){for(r=++l;o>r;r++)if(i.relative[e[r].type])break;return Ct(l>1&&wt(f),l>1&&xt(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(z,"$1"),n,r>l&&kt(e.slice(l,r)),o>r&&kt(e=e.slice(r)),o>r&&xt(e))}f.push(n)}return wt(f)}function Nt(e,t){var n=0,o=t.length>0,s=e.length>0,a=function(a,l,c,f,h){var d,g,m,y=[],v=0,x="0",b=a&&[],T=null!=h,C=u,k=a||s&&i.find.TAG("*",h&&l.parentNode||l),N=w+=null==C?1:Math.random()||.1;for(T&&(u=l!==p&&l,r=n);null!=(d=k[x]);x++){if(s&&d){g=0;while(m=e[g++])if(m(d,l,c)){f.push(d);break}T&&(w=N,r=++n)}o&&((d=!m&&d)&&v--,a&&b.push(d))}if(v+=x,o&&x!==v){g=0;while(m=t[g++])m(b,y,l,c);if(a){if(v>0)while(x--)b[x]||y[x]||(y[x]=H.call(f));y=Tt(y)}O.apply(f,y),T&&!a&&y.length>0&&v+t.length>1&&ot.uniqueSort(f)}return T&&(w=N,u=C),b};return o?ut(a):a}a=ot.compile=function(e,t){var n,r=[],i=[],o=N[e+" "];if(!o){t||(t=vt(e)),n=t.length;while(n--)o=kt(t[n]),o[v]?r.push(o):i.push(o);o=N(e,Nt(i,r))}return o};function Et(e,t,n){var r=0,i=t.length;for(;i>r;r++)ot(e,t[r],n);return n}function St(e,t,r,o){var s,u,l,c,p,f=vt(e);if(!o&&1===f.length){if(u=f[0]=f[0].slice(0),u.length>2&&"ID"===(l=u[0]).type&&n.getById&&9===t.nodeType&&h&&i.relative[u[1].type]){if(t=(i.find.ID(l.matches[0].replace(nt,rt),t)||[])[0],!t)return r;e=e.slice(u.shift().value.length)}s=J.needsContext.test(e)?0:u.length;while(s--){if(l=u[s],i.relative[c=l.type])break;if((p=i.find[c])&&(o=p(l.matches[0].replace(nt,rt),U.test(u[0].type)&&t.parentNode||t))){if(u.splice(s,1),e=o.length&&xt(u),!e)return O.apply(r,o),r;break}}}return a(e,f)(o,t,!h,r,U.test(e)),r}i.pseudos.nth=i.pseudos.eq;function jt(){}jt.prototype=i.filters=i.pseudos,i.setFilters=new jt,n.sortStable=v.split("").sort(S).join("")===v,c(),[0,0].sort(S),n.detectDuplicates=E,x.find=ot,x.expr=ot.selectors,x.expr[":"]=x.expr.pseudos,x.unique=ot.uniqueSort,x.text=ot.getText,x.isXMLDoc=ot.isXML,x.contains=ot.contains}(e);var D={};function A(e){var t=D[e]={};return x.each(e.match(w)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?D[e]||A(e):x.extend({},e);var t,n,r,i,o,s,a=[],u=!e.once&&[],l=function(p){for(t=e.memory&&p,n=!0,s=i||0,i=0,o=a.length,r=!0;a&&o>s;s++)if(a[s].apply(p[0],p[1])===!1&&e.stopOnFalse){t=!1;break}r=!1,a&&(u?u.length&&l(u.shift()):t?a=[]:c.disable())},c={add:function(){if(a){var n=a.length;(function s(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&c.has(n)||a.push(n):n&&n.length&&"string"!==r&&s(n)})})(arguments),r?o=a.length:t&&(i=n,l(t))}return this},remove:function(){return a&&x.each(arguments,function(e,t){var n;while((n=x.inArray(t,a,n))>-1)a.splice(n,1),r&&(o>=n&&o--,s>=n&&s--)}),this},has:function(e){return e?x.inArray(e,a)>-1:!(!a||!a.length)},empty:function(){return a=[],o=0,this},disable:function(){return a=u=t=undefined,this},disabled:function(){return!a},lock:function(){return u=undefined,t||c.disable(),this},locked:function(){return!u},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!a||n&&!u||(r?u.push(t):l(t)),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!n}};return c},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var s=o[0],a=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=a&&a.apply(this,arguments);e&&x.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[s+"With"](this===r?n.promise():this,a?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?x.extend(e,r):r}},i={};return r.pipe=r.then,x.each(t,function(e,o){var s=o[2],a=o[3];r[o[1]]=s.add,a&&s.add(function(){n=a},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=s.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=d.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),s=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?d.call(arguments):r,n===a?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},a,u,l;if(r>1)for(a=Array(r),u=Array(r),l=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(s(t,l,n)).fail(o.reject).progress(s(t,u,a)):--i;return i||o.resolveWith(l,n),o.promise()}}),x.support=function(t){var n=o.createElement("input"),r=o.createDocumentFragment(),i=o.createElement("div"),s=o.createElement("select"),a=s.appendChild(o.createElement("option"));return n.type?(n.type="checkbox",t.checkOn=""!==n.value,t.optSelected=a.selected,t.reliableMarginRight=!0,t.boxSizingReliable=!0,t.pixelPosition=!1,n.checked=!0,t.noCloneChecked=n.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!a.disabled,n=o.createElement("input"),n.value="t",n.type="radio",t.radioValue="t"===n.value,n.setAttribute("checked","t"),n.setAttribute("name","t"),r.appendChild(n),t.checkClone=r.cloneNode(!0).cloneNode(!0).lastChild.checked,t.focusinBubbles="onfocusin"in e,i.style.backgroundClip="content-box",i.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===i.style.backgroundClip,x(function(){var n,r,s="padding:0;margin:0;border:0;display:block;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box",a=o.getElementsByTagName("body")[0];a&&(n=o.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",a.appendChild(n).appendChild(i),i.innerHTML="",i.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%",x.swap(a,null!=a.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===i.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(i,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(i,null)||{width:"4px"}).width,r=i.appendChild(o.createElement("div")),r.style.cssText=i.style.cssText=s,r.style.marginRight=r.style.width="0",i.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),a.removeChild(n))}),t):t}({});var L,H,q=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,O=/([A-Z])/g;function F(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=x.expando+Math.random()}F.uid=1,F.accepts=function(e){return e.nodeType?1===e.nodeType||9===e.nodeType:!0},F.prototype={key:function(e){if(!F.accepts(e))return 0;var t={},n=e[this.expando];if(!n){n=F.uid++;try{t[this.expando]={value:n},Object.defineProperties(e,t)}catch(r){t[this.expando]=n,x.extend(e,t)}}return this.cache[n]||(this.cache[n]={}),n},set:function(e,t,n){var r,i=this.key(e),o=this.cache[i];if("string"==typeof t)o[t]=n;else if(x.isEmptyObject(o))x.extend(this.cache[i],t);else for(r in t)o[r]=t[r];return o},get:function(e,t){var n=this.cache[this.key(e)];return t===undefined?n:n[t]},access:function(e,t,n){return t===undefined||t&&"string"==typeof t&&n===undefined?this.get(e,t):(this.set(e,t,n),n!==undefined?n:t)},remove:function(e,t){var n,r,i,o=this.key(e),s=this.cache[o];if(t===undefined)this.cache[o]={};else{x.isArray(t)?r=t.concat(t.map(x.camelCase)):(i=x.camelCase(t),t in s?r=[t,i]:(r=i,r=r in s?[r]:r.match(w)||[])),n=r.length;while(n--)delete s[r[n]]}},hasData:function(e){return!x.isEmptyObject(this.cache[e[this.expando]]||{})},discard:function(e){e[this.expando]&&delete this.cache[e[this.expando]]}},L=new F,H=new F,x.extend({acceptData:F.accepts,hasData:function(e){return L.hasData(e)||H.hasData(e)},data:function(e,t,n){return L.access(e,t,n)},removeData:function(e,t){L.remove(e,t)},_data:function(e,t,n){return H.access(e,t,n)},_removeData:function(e,t){H.remove(e,t)}}),x.fn.extend({data:function(e,t){var n,r,i=this[0],o=0,s=null;if(e===undefined){if(this.length&&(s=L.get(i),1===i.nodeType&&!H.get(i,"hasDataAttrs"))){for(n=i.attributes;n.length>o;o++)r=n[o].name,0===r.indexOf("data-")&&(r=x.camelCase(r.slice(5)),P(i,r,s[r]));H.set(i,"hasDataAttrs",!0)}return s}return"object"==typeof e?this.each(function(){L.set(this,e)}):x.access(this,function(t){var n,r=x.camelCase(e);if(i&&t===undefined){if(n=L.get(i,e),n!==undefined)return n;if(n=L.get(i,r),n!==undefined)return n;if(n=P(i,r,undefined),n!==undefined)return n}else this.each(function(){var n=L.get(this,r);L.set(this,r,t),-1!==e.indexOf("-")&&n!==undefined&&L.set(this,e,t)})},null,t,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){L.remove(this,e)})}});function P(e,t,n){var r;if(n===undefined&&1===e.nodeType)if(r="data-"+t.replace(O,"-$1").toLowerCase(),n=e.getAttribute(r),"string"==typeof n){try{n="true"===n?!0:"false"===n?!1:"null"===n?null:+n+""===n?+n:q.test(n)?JSON.parse(n):n}catch(i){}L.set(e,t,n)}else n=undefined;return n}x.extend({queue:function(e,t,n){var r;return e?(t=(t||"fx")+"queue",r=H.get(e,t),n&&(!r||x.isArray(n)?r=H.access(e,t,x.makeArray(n)):r.push(n)),r||[]):undefined},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),s=function(){x.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,s,o)),!r&&o&&o.empty.fire() +},_queueHooks:function(e,t){var n=t+"queueHooks";return H.get(e,n)||H.access(e,n,{empty:x.Callbacks("once memory").add(function(){H.remove(e,[t+"queue",n])})})}}),x.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),n>arguments.length?x.queue(this[0],e):t===undefined?this:this.each(function(){var n=x.queue(this,e,t);x._queueHooks(this,e),"fx"===e&&"inprogress"!==n[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=x.Deferred(),o=this,s=this.length,a=function(){--r||i.resolveWith(o,[o])};"string"!=typeof e&&(t=e,e=undefined),e=e||"fx";while(s--)n=H.get(o[s],e+"queueHooks"),n&&n.empty&&(r++,n.empty.add(a));return a(),i.promise(t)}});var R,M,W=/[\t\r\n\f]/g,$=/\r/g,B=/^(?:input|select|textarea|button)$/i;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[x.propFix[e]||e]})},addClass:function(e){var t,n,r,i,o,s=0,a=this.length,u="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];a>s;s++)if(n=this[s],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(W," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,s=0,a=this.length,u=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];a>s;s++)if(n=this[s],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(W," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e,i="boolean"==typeof t;return x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var o,s=0,a=x(this),u=t,l=e.match(w)||[];while(o=l[s++])u=i?u:!a.hasClass(o),a[u?"addClass":"removeClass"](o)}else(n===r||"boolean"===n)&&(this.className&&H.set(this,"__className__",this.className),this.className=this.className||e===!1?"":H.get(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(W," ").indexOf(t)>=0)return!0;return!1},val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=x.isFunction(e),this.each(function(n){var i;1===this.nodeType&&(i=r?e.call(this,n,x(this).val()):e,null==i?i="":"number"==typeof i?i+="":x.isArray(i)&&(i=x.map(i,function(e){return null==e?"":e+""})),t=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],t&&"set"in t&&t.set(this,i,"value")!==undefined||(this.value=i))});if(i)return t=x.valHooks[i.type]||x.valHooks[i.nodeName.toLowerCase()],t&&"get"in t&&(n=t.get(i,"value"))!==undefined?n:(n=i.value,"string"==typeof n?n.replace($,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,s=o?null:[],a=o?i+1:r.length,u=0>i?a:o?i:0;for(;a>u;u++)if(n=r[u],!(!n.selected&&u!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),s=i.length;while(s--)r=i[s],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,t,n){var i,o,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===r?x.prop(e,t,n):(1===s&&x.isXMLDoc(e)||(t=t.toLowerCase(),i=x.attrHooks[t]||(x.expr.match.bool.test(t)?M:R)),n===undefined?i&&"get"in i&&null!==(o=i.get(e,t))?o:(o=x.find.attr(e,t),null==o?undefined:o):null!==n?i&&"set"in i&&(o=i.set(e,n,t))!==undefined?o:(e.setAttribute(t,n+""),n):(x.removeAttr(e,t),undefined))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(w);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.bool.test(n)&&(e[r]=!1),e.removeAttribute(n)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,t,n){var r,i,o,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return o=1!==s||!x.isXMLDoc(e),o&&(t=x.propFix[t]||t,i=x.propHooks[t]),n!==undefined?i&&"set"in i&&(r=i.set(e,n,t))!==undefined?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){return e.hasAttribute("tabindex")||B.test(e.nodeName)||e.href?e.tabIndex:-1}}}}),M={set:function(e,t,n){return t===!1?x.removeAttr(e,n):e.setAttribute(n,n),n}},x.each(x.expr.match.bool.source.match(/\w+/g),function(e,t){var n=x.expr.attrHandle[t]||x.find.attr;x.expr.attrHandle[t]=function(e,t,r){var i=x.expr.attrHandle[t],o=r?undefined:(x.expr.attrHandle[t]=undefined)!=n(e,t,r)?t.toLowerCase():null;return x.expr.attrHandle[t]=i,o}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,t){return x.isArray(t)?e.checked=x.inArray(x(e).val(),t)>=0:undefined}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var I=/^key/,z=/^(?:mouse|contextmenu)|click/,_=/^(?:focusinfocus|focusoutblur)$/,X=/^([^.]*)(?:\.(.+)|)$/;function U(){return!0}function Y(){return!1}function V(){try{return o.activeElement}catch(e){}}x.event={global:{},add:function(e,t,n,i,o){var s,a,u,l,c,p,f,h,d,g,m,y=H.get(e);if(y){n.handler&&(s=n,n=s.handler,o=s.selector),n.guid||(n.guid=x.guid++),(l=y.events)||(l=y.events={}),(a=y.handle)||(a=y.handle=function(e){return typeof x===r||e&&x.event.triggered===e.type?undefined:x.event.dispatch.apply(a.elem,arguments)},a.elem=e),t=(t||"").match(w)||[""],c=t.length;while(c--)u=X.exec(t[c])||[],d=m=u[1],g=(u[2]||"").split(".").sort(),d&&(f=x.event.special[d]||{},d=(o?f.delegateType:f.bindType)||d,f=x.event.special[d]||{},p=x.extend({type:d,origType:m,data:i,handler:n,guid:n.guid,selector:o,needsContext:o&&x.expr.match.needsContext.test(o),namespace:g.join(".")},s),(h=l[d])||(h=l[d]=[],h.delegateCount=0,f.setup&&f.setup.call(e,i,g,a)!==!1||e.addEventListener&&e.addEventListener(d,a,!1)),f.add&&(f.add.call(e,p),p.handler.guid||(p.handler.guid=n.guid)),o?h.splice(h.delegateCount++,0,p):h.push(p),x.event.global[d]=!0);e=null}},remove:function(e,t,n,r,i){var o,s,a,u,l,c,p,f,h,d,g,m=H.hasData(e)&&H.get(e);if(m&&(u=m.events)){t=(t||"").match(w)||[""],l=t.length;while(l--)if(a=X.exec(t[l])||[],h=g=a[1],d=(a[2]||"").split(".").sort(),h){p=x.event.special[h]||{},h=(r?p.delegateType:p.bindType)||h,f=u[h]||[],a=a[2]&&RegExp("(^|\\.)"+d.join("\\.(?:.*\\.|)")+"(\\.|$)"),s=o=f.length;while(o--)c=f[o],!i&&g!==c.origType||n&&n.guid!==c.guid||a&&!a.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(f.splice(o,1),c.selector&&f.delegateCount--,p.remove&&p.remove.call(e,c));s&&!f.length&&(p.teardown&&p.teardown.call(e,d,m.handle)!==!1||x.removeEvent(e,h,m.handle),delete u[h])}else for(h in u)x.event.remove(e,h+t[l],n,r,!0);x.isEmptyObject(u)&&(delete m.handle,H.remove(e,"events"))}},trigger:function(t,n,r,i){var s,a,u,l,c,p,f,h=[r||o],d=y.call(t,"type")?t.type:t,g=y.call(t,"namespace")?t.namespace.split("."):[];if(a=u=r=r||o,3!==r.nodeType&&8!==r.nodeType&&!_.test(d+x.event.triggered)&&(d.indexOf(".")>=0&&(g=d.split("."),d=g.shift(),g.sort()),c=0>d.indexOf(":")&&"on"+d,t=t[x.expando]?t:new x.Event(d,"object"==typeof t&&t),t.isTrigger=i?2:3,t.namespace=g.join("."),t.namespace_re=t.namespace?RegExp("(^|\\.)"+g.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=undefined,t.target||(t.target=r),n=null==n?[t]:x.makeArray(n,[t]),f=x.event.special[d]||{},i||!f.trigger||f.trigger.apply(r,n)!==!1)){if(!i&&!f.noBubble&&!x.isWindow(r)){for(l=f.delegateType||d,_.test(l+d)||(a=a.parentNode);a;a=a.parentNode)h.push(a),u=a;u===(r.ownerDocument||o)&&h.push(u.defaultView||u.parentWindow||e)}s=0;while((a=h[s++])&&!t.isPropagationStopped())t.type=s>1?l:f.bindType||d,p=(H.get(a,"events")||{})[t.type]&&H.get(a,"handle"),p&&p.apply(a,n),p=c&&a[c],p&&x.acceptData(a)&&p.apply&&p.apply(a,n)===!1&&t.preventDefault();return t.type=d,i||t.isDefaultPrevented()||f._default&&f._default.apply(h.pop(),n)!==!1||!x.acceptData(r)||c&&x.isFunction(r[d])&&!x.isWindow(r)&&(u=r[c],u&&(r[c]=null),x.event.triggered=d,r[d](),x.event.triggered=undefined,u&&(r[c]=u)),t.result}},dispatch:function(e){e=x.event.fix(e);var t,n,r,i,o,s=[],a=d.call(arguments),u=(H.get(this,"events")||{})[e.type]||[],l=x.event.special[e.type]||{};if(a[0]=e,e.delegateTarget=this,!l.preDispatch||l.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),t=0;while((i=s[t++])&&!e.isPropagationStopped()){e.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(o.namespace))&&(e.handleObj=o,e.data=o.data,r=((x.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,a),r!==undefined&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return l.postDispatch&&l.postDispatch.call(this,e),e.result}},handlers:function(e,t){var n,r,i,o,s=[],a=t.delegateCount,u=e.target;if(a&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!==this;u=u.parentNode||this)if(u.disabled!==!0||"click"!==e.type){for(r=[],n=0;a>n;n++)o=t[n],i=o.selector+" ",r[i]===undefined&&(r[i]=o.needsContext?x(i,this).index(u)>=0:x.find(i,this,null,[u]).length),r[i]&&r.push(o);r.length&&s.push({elem:u,handlers:r})}return t.length>a&&s.push({elem:this,handlers:t.slice(a)}),s},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,t){var n,r,i,s=t.button;return null==e.pageX&&null!=t.clientX&&(n=e.target.ownerDocument||o,r=n.documentElement,i=n.body,e.pageX=t.clientX+(r&&r.scrollLeft||i&&i.scrollLeft||0)-(r&&r.clientLeft||i&&i.clientLeft||0),e.pageY=t.clientY+(r&&r.scrollTop||i&&i.scrollTop||0)-(r&&r.clientTop||i&&i.clientTop||0)),e.which||s===undefined||(e.which=1&s?1:2&s?3:4&s?2:0),e}},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,s=e,a=this.fixHooks[i];a||(this.fixHooks[i]=a=z.test(i)?this.mouseHooks:I.test(i)?this.keyHooks:{}),r=a.props?this.props.concat(a.props):this.props,e=new x.Event(s),t=r.length;while(t--)n=r[t],e[n]=s[n];return e.target||(e.target=o),3===e.target.nodeType&&(e.target=e.target.parentNode),a.filter?a.filter(e,s):e},special:{load:{noBubble:!0},focus:{trigger:function(){return this!==V()&&this.focus?(this.focus(),!1):undefined},delegateType:"focusin"},blur:{trigger:function(){return this===V()&&this.blur?(this.blur(),!1):undefined},delegateType:"focusout"},click:{trigger:function(){return"checkbox"===this.type&&this.click&&x.nodeName(this,"input")?(this.click(),!1):undefined},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==undefined&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)},x.Event=function(e,t){return this instanceof x.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.getPreventDefault&&e.getPreventDefault()?U:Y):this.type=e,t&&x.extend(this,t),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,undefined):new x.Event(e,t)},x.Event.prototype={isDefaultPrevented:Y,isPropagationStopped:Y,isImmediatePropagationStopped:Y,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=U,e&&e.preventDefault&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=U,e&&e.stopPropagation&&e.stopPropagation()},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=U,this.stopPropagation()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&o.addEventListener(e,r,!0)},teardown:function(){0===--n&&o.removeEventListener(e,r,!0)}}}),x.fn.extend({on:function(e,t,n,r,i){var o,s;if("object"==typeof e){"string"!=typeof t&&(n=n||t,t=undefined);for(s in e)this.on(s,t,n,e[s],i);return this}if(null==n&&null==r?(r=t,n=t=undefined):null==r&&("string"==typeof t?(r=n,n=undefined):(r=n,n=t,t=undefined)),r===!1)r=Y;else if(!r)return this;return 1===i&&(o=r,r=function(e){return x().off(e),o.apply(this,arguments)},r.guid=o.guid||(o.guid=x.guid++)),this.each(function(){x.event.add(this,e,r,n,t)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,x(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return(t===!1||"function"==typeof t)&&(n=t,t=undefined),n===!1&&(n=Y),this.each(function(){x.event.remove(this,e,n,t)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];return n?x.event.trigger(e,t,n,!0):undefined}});var G=/^.[^:#\[\.,]*$/,J=/^(?:parents|prev(?:Until|All))/,Q=x.expr.match.needsContext,K={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(x(e).filter(function(){for(t=0;i>t;t++)if(x.contains(r[t],this))return!0}));for(t=0;i>t;t++)x.find(e,r[t],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},has:function(e){var t=x(e,this),n=t.length;return this.filter(function(){var e=0;for(;n>e;e++)if(x.contains(this,t[e]))return!0})},not:function(e){return this.pushStack(et(this,e||[],!0))},filter:function(e){return this.pushStack(et(this,e||[],!1))},is:function(e){return!!et(this,"string"==typeof e&&Q.test(e)?x(e):e||[],!1).length},closest:function(e,t){var n,r=0,i=this.length,o=[],s=Q.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(s?s.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?g.call(x(e),this[0]):g.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function Z(e,t){while((e=e[t])&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return Z(e,"nextSibling")},prev:function(e){return Z(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return e.contentDocument||x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(K[e]||x.unique(i),J.test(e)&&i.reverse()),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,t,n){var r=[],i=n!==undefined;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&x(e).is(n))break;r.push(e)}return r},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function et(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(G.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return g.call(t,e)>=0!==n})}var tt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,nt=/<([\w:]+)/,rt=/<|&#?\w+;/,it=/<(?:script|style|link)/i,ot=/^(?:checkbox|radio)$/i,st=/checked\s*(?:[^=]|=\s*.checked.)/i,at=/^$|\/(?:java|ecma)script/i,ut=/^true\/(.*)/,lt=/^\s*\s*$/g,ct={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ct.optgroup=ct.option,ct.tbody=ct.tfoot=ct.colgroup=ct.caption=ct.thead,ct.th=ct.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===undefined?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||o).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=pt(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=pt(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(mt(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&dt(mt(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++)1===e.nodeType&&(x.cleanData(mt(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return x.clone(this,e,t)})},html:function(e){return x.access(this,function(e){var t=this[0]||{},n=0,r=this.length;if(e===undefined&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!it.test(e)&&!ct[(nt.exec(e)||["",""])[1].toLowerCase()]){e=e.replace(tt,"<$1>");try{for(;r>n;n++)t=this[n]||{},1===t.nodeType&&(x.cleanData(mt(t,!1)),t.innerHTML=e);t=0}catch(i){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(r&&r.parentNode!==i&&(r=this.nextSibling),x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=f.apply([],e);var r,i,o,s,a,u,l=0,c=this.length,p=this,h=c-1,d=e[0],g=x.isFunction(d);if(g||!(1>=c||"string"!=typeof d||x.support.checkClone)&&st.test(d))return this.each(function(r){var i=p.eq(r);g&&(e[0]=d.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(r=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),i=r.firstChild,1===r.childNodes.length&&(r=i),i)){for(o=x.map(mt(r,"script"),ft),s=o.length;c>l;l++)a=r,l!==h&&(a=x.clone(a,!0,!0),s&&x.merge(o,mt(a,"script"))),t.call(this[l],a,l);if(s)for(u=o[o.length-1].ownerDocument,x.map(o,ht),l=0;s>l;l++)a=o[l],at.test(a.type||"")&&!H.access(a,"globalEval")&&x.contains(u,a)&&(a.src?x._evalUrl(a.src):x.globalEval(a.textContent.replace(lt,"")))}return this}}),x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=[],i=x(e),o=i.length-1,s=0;for(;o>=s;s++)n=s===o?this:this.clone(!0),x(i[s])[t](n),h.apply(r,n.get());return this.pushStack(r)}}),x.extend({clone:function(e,t,n){var r,i,o,s,a=e.cloneNode(!0),u=x.contains(e.ownerDocument,e);if(!(x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(s=mt(a),o=mt(e),r=0,i=o.length;i>r;r++)yt(o[r],s[r]);if(t)if(n)for(o=o||mt(e),s=s||mt(a),r=0,i=o.length;i>r;r++)gt(o[r],s[r]);else gt(e,a);return s=mt(a,"script"),s.length>0&&dt(s,!u&&mt(e,"script")),a},buildFragment:function(e,t,n,r){var i,o,s,a,u,l,c=0,p=e.length,f=t.createDocumentFragment(),h=[];for(;p>c;c++)if(i=e[c],i||0===i)if("object"===x.type(i))x.merge(h,i.nodeType?[i]:i);else if(rt.test(i)){o=o||f.appendChild(t.createElement("div")),s=(nt.exec(i)||["",""])[1].toLowerCase(),a=ct[s]||ct._default,o.innerHTML=a[1]+i.replace(tt,"<$1>")+a[2],l=a[0];while(l--)o=o.firstChild;x.merge(h,o.childNodes),o=f.firstChild,o.textContent=""}else h.push(t.createTextNode(i));f.textContent="",c=0;while(i=h[c++])if((!r||-1===x.inArray(i,r))&&(u=x.contains(i.ownerDocument,i),o=mt(f.appendChild(i),"script"),u&&dt(o),n)){l=0;while(i=o[l++])at.test(i.type||"")&&n.push(i)}return f},cleanData:function(e){var t,n,r,i,o,s,a=x.event.special,u=0;for(;(n=e[u])!==undefined;u++){if(F.accepts(n)&&(o=n[H.expando],o&&(t=H.cache[o]))){if(r=Object.keys(t.events||{}),r.length)for(s=0;(i=r[s])!==undefined;s++)a[i]?x.event.remove(n,i):x.removeEvent(n,i,t.handle);H.cache[o]&&delete H.cache[o]}delete L.cache[n[L.expando]]}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})}});function pt(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function ft(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function ht(e){var t=ut.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function dt(e,t){var n=e.length,r=0;for(;n>r;r++)H.set(e[r],"globalEval",!t||H.get(t[r],"globalEval"))}function gt(e,t){var n,r,i,o,s,a,u,l;if(1===t.nodeType){if(H.hasData(e)&&(o=H.access(e),s=H.set(t,o),l=o.events)){delete s.handle,s.events={};for(i in l)for(n=0,r=l[i].length;r>n;n++)x.event.add(t,i,l[i][n])}L.hasData(e)&&(a=L.access(e),u=x.extend({},a),L.set(t,u))}}function mt(e,t){var n=e.getElementsByTagName?e.getElementsByTagName(t||"*"):e.querySelectorAll?e.querySelectorAll(t||"*"):[];return t===undefined||t&&x.nodeName(e,t)?x.merge([e],n):n}function yt(e,t){var n=t.nodeName.toLowerCase();"input"===n&&ot.test(e.type)?t.checked=e.checked:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}x.fn.extend({wrapAll:function(e){var t;return x.isFunction(e)?this.each(function(t){x(this).wrapAll(e.call(this,t))}):(this[0]&&(t=x(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this)},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var vt,xt,bt=/^(none|table(?!-c[ea]).+)/,wt=/^margin/,Tt=RegExp("^("+b+")(.*)$","i"),Ct=RegExp("^("+b+")(?!px)[a-z%]+$","i"),kt=RegExp("^([+-])=("+b+")","i"),Nt={BODY:"block"},Et={position:"absolute",visibility:"hidden",display:"block"},St={letterSpacing:0,fontWeight:400},jt=["Top","Right","Bottom","Left"],Dt=["Webkit","O","Moz","ms"];function At(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=Dt.length;while(i--)if(t=Dt[i]+n,t in e)return t;return r}function Lt(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function Ht(t){return e.getComputedStyle(t,null)}function qt(e,t){var n,r,i,o=[],s=0,a=e.length;for(;a>s;s++)r=e[s],r.style&&(o[s]=H.get(r,"olddisplay"),n=r.style.display,t?(o[s]||"none"!==n||(r.style.display=""),""===r.style.display&&Lt(r)&&(o[s]=H.access(r,"olddisplay",Rt(r.nodeName)))):o[s]||(i=Lt(r),(n&&"none"!==n||!i)&&H.set(r,"olddisplay",i?n:x.css(r,"display"))));for(s=0;a>s;s++)r=e[s],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[s]||"":"none"));return e}x.fn.extend({css:function(e,t){return x.access(this,function(e,t,n){var r,i,o={},s=0;if(x.isArray(t)){for(r=Ht(e),i=t.length;i>s;s++)o[t[s]]=x.css(e,t[s],!1,r);return o}return n!==undefined?x.style(e,t,n):x.css(e,t)},e,t,arguments.length>1)},show:function(){return qt(this,!0)},hide:function(){return qt(this)},toggle:function(e){var t="boolean"==typeof e;return this.each(function(){(t?e:Lt(this))?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=vt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":"cssFloat"},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,s,a=x.camelCase(t),u=e.style;return t=x.cssProps[a]||(x.cssProps[a]=At(u,a)),s=x.cssHooks[t]||x.cssHooks[a],n===undefined?s&&"get"in s&&(i=s.get(e,!1,r))!==undefined?i:u[t]:(o=typeof n,"string"===o&&(i=kt.exec(n))&&(n=(i[1]+1)*i[2]+parseFloat(x.css(e,t)),o="number"),null==n||"number"===o&&isNaN(n)||("number"!==o||x.cssNumber[a]||(n+="px"),x.support.clearCloneStyle||""!==n||0!==t.indexOf("background")||(u[t]="inherit"),s&&"set"in s&&(n=s.set(e,n,r))===undefined||(u[t]=n)),undefined)}},css:function(e,t,n,r){var i,o,s,a=x.camelCase(t);return t=x.cssProps[a]||(x.cssProps[a]=At(e.style,a)),s=x.cssHooks[t]||x.cssHooks[a],s&&"get"in s&&(i=s.get(e,!0,n)),i===undefined&&(i=vt(e,t,r)),"normal"===i&&t in St&&(i=St[t]),""===n||n?(o=parseFloat(i),n===!0||x.isNumeric(o)?o||0:i):i}}),vt=function(e,t,n){var r,i,o,s=n||Ht(e),a=s?s.getPropertyValue(t)||s[t]:undefined,u=e.style;return s&&(""!==a||x.contains(e.ownerDocument,e)||(a=x.style(e,t)),Ct.test(a)&&wt.test(t)&&(r=u.width,i=u.minWidth,o=u.maxWidth,u.minWidth=u.maxWidth=u.width=a,a=s.width,u.width=r,u.minWidth=i,u.maxWidth=o)),a};function Ot(e,t,n){var r=Tt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function Ft(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,s=0;for(;4>o;o+=2)"margin"===n&&(s+=x.css(e,n+jt[o],!0,i)),r?("content"===n&&(s-=x.css(e,"padding"+jt[o],!0,i)),"margin"!==n&&(s-=x.css(e,"border"+jt[o]+"Width",!0,i))):(s+=x.css(e,"padding"+jt[o],!0,i),"padding"!==n&&(s+=x.css(e,"border"+jt[o]+"Width",!0,i)));return s}function Pt(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Ht(e),s=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=vt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Ct.test(i))return i;r=s&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+Ft(e,t,n||(s?"border":"content"),r,o)+"px"}function Rt(e){var t=o,n=Nt[e];return n||(n=Mt(e,t),"none"!==n&&n||(xt=(xt||x("

T?l+QGi$fkPR)FX&X7jjq1=NyC`9!-G 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≻eY57dL8+{AKtzjXxKp@;eV8t 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